Update {fmt} to 7.1.3
|
@ -1,10 +1,8 @@
|
|||
cmake_minimum_required(VERSION 3.1.0)
|
||||
cmake_minimum_required(VERSION 3.1...3.18)
|
||||
|
||||
# Use newer policies if available, up to most recent tested version of CMake.
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.11)
|
||||
# Fallback for using newer policies on CMake <3.12.
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.12)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.11)
|
||||
endif()
|
||||
|
||||
# Determine if fmt is built as a subproject (using add_subdirectory)
|
||||
|
@ -55,8 +53,8 @@ endif ()
|
|||
project(FMT CXX)
|
||||
include(GNUInstallDirs)
|
||||
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
|
||||
"Installation directory for include files, a relative path "
|
||||
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
||||
"Installation directory for include files, a relative path that "
|
||||
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.")
|
||||
|
||||
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
|
||||
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
|
||||
|
@ -109,11 +107,11 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|||
-Wold-style-cast -Wundef
|
||||
-Wredundant-decls -Wwrite-strings -Wpointer-arith
|
||||
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
|
||||
-Wcast-align -Wnon-virtual-dtor
|
||||
-Wcast-align
|
||||
-Wctor-dtor-privacy -Wdisabled-optimization
|
||||
-Winvalid-pch -Woverloaded-virtual
|
||||
-Wconversion -Wswitch-enum
|
||||
-Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow)
|
||||
-Wconversion -Wswitch-enum -Wundef
|
||||
-Wno-ctor-dtor-privacy -Wno-format-nonliteral)
|
||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
|
||||
-Wno-dangling-else -Wno-unused-local-typedefs)
|
||||
|
@ -131,8 +129,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|||
endif ()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion
|
||||
-Wno-sign-conversion -Wdeprecated -Wweak-vtables)
|
||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef
|
||||
-Wdeprecated -Wweak-vtables)
|
||||
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
|
||||
if (HAS_NULLPTR_WARNING)
|
||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
|
||||
|
@ -201,7 +199,10 @@ if (HAVE_STRTOD_L)
|
|||
endif ()
|
||||
|
||||
if (MINGW)
|
||||
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
|
||||
check_cxx_compiler_flag("Wa,-mbig-obj" FMT_HAS_MBIG_OBJ)
|
||||
if (${FMT_HAS_MBIG_OBJ})
|
||||
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (FMT_WERROR)
|
||||
|
@ -231,7 +232,8 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
|||
endif ()
|
||||
|
||||
if (BUILD_SHARED_LIBS)
|
||||
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND NOT EMSCRIPTEN)
|
||||
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND
|
||||
NOT EMSCRIPTEN)
|
||||
# Fix rpmlint warning:
|
||||
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
|
||||
target_link_libraries(fmt -Wl,--as-needed)
|
||||
|
@ -256,20 +258,22 @@ target_include_directories(fmt-header-only INTERFACE
|
|||
if (FMT_INSTALL)
|
||||
include(CMakePackageConfigHelpers)
|
||||
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
||||
"Installation directory for cmake files, a relative path "
|
||||
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
||||
"Installation directory for cmake files, a relative path that "
|
||||
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute "
|
||||
"path.")
|
||||
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
|
||||
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
|
||||
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
|
||||
set(targets_export_name fmt-targets)
|
||||
|
||||
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
||||
"Installation directory for libraries, a relative path "
|
||||
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
||||
"Installation directory for libraries, a relative path that "
|
||||
"will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.")
|
||||
|
||||
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
|
||||
"Installation directory for pkgconfig (.pc) files, a relative path "
|
||||
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
||||
"Installation directory for pkgconfig (.pc) files, a relative "
|
||||
"path that will be joined with ${CMAKE_INSTALL_PREFIX} or an "
|
||||
"absolute path.")
|
||||
|
||||
# Generate the version, config and target files into the build directory.
|
||||
write_basic_package_version_file(
|
||||
|
@ -326,6 +330,12 @@ endif ()
|
|||
# Control fuzzing independent of the unit tests.
|
||||
if (FMT_FUZZ)
|
||||
add_subdirectory(test/fuzzing)
|
||||
|
||||
# The FMT_FUZZ macro is used to prevent resource exhaustion in fuzzing
|
||||
# mode and make fuzzing practically possible. It is similar to
|
||||
# FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION but uses a different name to
|
||||
# avoid interfering with fuzzing of projects that use {fmt}.
|
||||
# See also https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode.
|
||||
target_compile_definitions(fmt PUBLIC FMT_FUZZ)
|
||||
endif ()
|
||||
|
||||
|
|
|
@ -14,4 +14,7 @@ exceptions:
|
|||
* snake_case should be used instead of UpperCamelCase for function and type
|
||||
names
|
||||
|
||||
All documentation must adhere to the [Google Developer Documentation Style
|
||||
Guide](https://developers.google.com/style).
|
||||
|
||||
Thanks for contributing!
|
||||
|
|
|
@ -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
|
||||
------------------
|
||||
|
||||
|
@ -49,7 +474,7 @@
|
|||
<http://www.zverovich.net/2020/05/21/reducing-library-size.html>`_.
|
||||
|
||||
* 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++
|
||||
|
||||
|
@ -188,7 +613,7 @@
|
|||
|
||||
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``
|
||||
(`#1655 <https://github.com/fmtlib/fmt/issues/1655>`_,
|
||||
`#1663 <https://github.com/fmtlib/fmt/pull/1663>`_,
|
||||
|
@ -1705,7 +2130,7 @@
|
|||
|
||||
* Implemented ``constexpr`` parsing of format strings and `compile-time format
|
||||
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
|
||||
|
||||
.. code:: c++
|
||||
|
@ -1766,7 +2191,7 @@
|
|||
throw format_error("invalid specifier");
|
||||
|
||||
* Added `iterator support
|
||||
<https://fmt.dev/dev/api.html#output-iterator-support>`_:
|
||||
<https://fmt.dev/latest/api.html#output-iterator-support>`_:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
|
@ -1777,7 +2202,7 @@
|
|||
fmt::format_to(std::back_inserter(out), "{}", 42);
|
||||
|
||||
* 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
|
||||
(`#298 <https://github.com/fmtlib/fmt/issues/298>`_):
|
||||
|
||||
|
@ -1788,7 +2213,7 @@
|
|||
// out == "1234" (without terminating '\0')
|
||||
|
||||
* 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:
|
||||
|
||||
.. code:: c++
|
||||
|
@ -1798,7 +2223,7 @@
|
|||
auto size = fmt::formatted_size("{}", 12345); // size == 5
|
||||
|
||||
* 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++
|
||||
|
||||
|
@ -1810,7 +2235,7 @@
|
|||
<https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_.
|
||||
|
||||
* 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:
|
||||
|
||||
.. code:: c++
|
||||
|
@ -1892,7 +2317,7 @@
|
|||
fmt::format("{} {two}", 1, fmt::arg("two", 2));
|
||||
|
||||
* 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.
|
||||
|
||||
* Disallowed formatting of multibyte strings into a wide character target
|
||||
|
|
270
fmt/README.rst
|
@ -7,48 +7,57 @@
|
|||
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
|
||||
:target: https://ci.appveyor.com/project/vitaut/fmt
|
||||
|
||||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
|
||||
:alt: fmt is continuously fuzzed att oss-fuzz
|
||||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
|
||||
:alt: fmt is continuously fuzzed at oss-fuzz
|
||||
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
|
||||
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
|
||||
:alt: Ask questions at StackOverflow with the tag fmt
|
||||
:target: https://stackoverflow.com/questions/tagged/fmt
|
||||
|
||||
**{fmt}** is an open-source formatting library for C++.
|
||||
It can be used as a safe and fast alternative to (s)printf and iostreams.
|
||||
**{fmt}** is an open-source formatting library providing a fast and safe
|
||||
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
|
||||
<https://stackoverflow.com/questions/tagged/fmt>`_.
|
||||
|
||||
Try {fmt} in `Compiler Explorer <https://godbolt.org/z/Eq5763>`_.
|
||||
|
||||
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
|
||||
* Implementation of `C++20 std::format
|
||||
<https://en.cppreference.com/w/cpp/utility/format>`__
|
||||
* `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
|
||||
of Python's
|
||||
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's
|
||||
`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
|
||||
<https://fmt.dev/latest/api.html#printf-formatting>`_ including
|
||||
the POSIX extension for positional arguments
|
||||
* Extensibility: support for user-defined types
|
||||
<https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX
|
||||
extension for positional arguments
|
||||
* 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
|
||||
`printf <https://en.cppreference.com/w/cpp/io/c/fprintf>`_,
|
||||
iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_ and
|
||||
`Converting a hundred million integers to strings per second
|
||||
``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_
|
||||
and `Converting a hundred million integers to strings per second
|
||||
<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
|
||||
consists of just three header files, ``core.h``, ``format.h`` and
|
||||
``format-inl.h``) and compiled code. See `Compile time and code bloat`_
|
||||
* Reliability: the library has an extensive set of `unit tests
|
||||
<https://github.com/fmtlib/fmt/tree/master/test>`_ and is continuously fuzzed
|
||||
* Small code size both in terms of source code with the minimum configuration
|
||||
consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``,
|
||||
and compiled code; see `Compile time and code bloat`_
|
||||
* Reliability: the library has an extensive set of `tests
|
||||
<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
|
||||
reported at compile time, automatic memory management prevents buffer overflow
|
||||
errors
|
||||
|
@ -57,18 +66,17 @@ Features
|
|||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
|
||||
* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
|
||||
consistent output across platforms and support for older compilers
|
||||
* Clean warning-free codebase even on high warning levels
|
||||
(``-Wall -Wextra -pedantic``)
|
||||
* Clean warning-free codebase even on high warning levels such as
|
||||
``-Wall -Wextra -pedantic``
|
||||
* Locale-independence by default
|
||||
* Support for wide strings
|
||||
* 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
|
||||
--------
|
||||
|
||||
Print ``Hello, world!`` to ``stdout``:
|
||||
**Print to stdout** (`run <https://godbolt.org/z/Tevcjh>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
|
@ -78,100 +86,95 @@ Print ``Hello, world!`` to ``stdout``:
|
|||
fmt::print("Hello, world!\n");
|
||||
}
|
||||
|
||||
Format a string:
|
||||
**Format a string** (`run <https://godbolt.org/z/oK8h33>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
std::string s = fmt::format("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++
|
||||
|
||||
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
|
||||
// s == "I'd rather be happy than right."
|
||||
|
||||
Print a chrono duration:
|
||||
**Print chrono durations** (`run <https://godbolt.org/z/K8s4Mc>`_)
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
int main() {
|
||||
using namespace std::chrono_literals;
|
||||
fmt::print("Elapsed time: {}", 42ms);
|
||||
using namespace std::literals::chrono_literals;
|
||||
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++
|
||||
|
||||
// test.cc
|
||||
#include <fmt/format.h>
|
||||
std::string s = format(FMT_STRING("{:d}"), "hello");
|
||||
#include <vector>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
gives a compile-time error because ``d`` is an invalid format specifier for a
|
||||
string.
|
||||
|
||||
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...));
|
||||
int main() {
|
||||
std::vector<int> v = {1, 2, 3};
|
||||
fmt::print("{}\n", v);
|
||||
}
|
||||
|
||||
report_error("file not found: {}", path);
|
||||
Output::
|
||||
|
||||
Note that ``vreport_error`` is not parameterized on argument types which can
|
||||
improve compile times and reduce code size compared to a fully parameterized
|
||||
version.
|
||||
{1, 2, 3}
|
||||
|
||||
**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
|
||||
----------
|
||||
|
@ -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
|
||||
<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>`_)
|
||||
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
|
||||
:target: https://fmt.dev/unknown_mac64_clang10.0.html
|
||||
.. image:: https://user-images.githubusercontent.com/576385/
|
||||
95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png
|
||||
:target: https://fmt.dev/unknown_mac64_clang12.0.html
|
||||
|
||||
Compile time and code bloat
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -282,30 +287,33 @@ or the bloat test::
|
|||
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
|
||||
|
||||
* `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>`_:
|
||||
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
|
||||
|
||||
* `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
|
||||
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)
|
||||
|
||||
* `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>`_:
|
||||
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>`_:
|
||||
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
|
||||
|
||||
* `OpenSpace <https://openspaceproject.com/>`_: An open-source
|
||||
* `OpenSpace <https://openspaceproject.com/>`_: an open-source
|
||||
astrovisualization framework
|
||||
|
||||
* `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
|
||||
|
||||
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
|
||||
* `quasardb <https://www.quasardb.net/>`_: a distributed, high-performance,
|
||||
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
|
||||
|
||||
* `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++
|
||||
|
||||
* `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
|
||||
|
||||
* `Salesforce Analytics Cloud
|
||||
<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
|
||||
|
||||
* `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
|
||||
|
||||
* `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
|
||||
|
||||
* `Windows Terminal <https://github.com/microsoft/terminal>`_: The new Windows
|
||||
Terminal
|
||||
* `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows
|
||||
terminal
|
||||
|
||||
`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
|
||||
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
|
||||
`Benchmarks`_).
|
||||
|
||||
|
|
|
@ -4,10 +4,14 @@ if (NOT DOXYGEN)
|
|||
return ()
|
||||
endif ()
|
||||
|
||||
find_package(PythonInterp QUIET REQUIRED)
|
||||
|
||||
add_custom_target(doc
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.py ${FMT_VERSION}
|
||||
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/build.py
|
||||
${FMT_VERSION}
|
||||
SOURCES api.rst syntax.rst usage.rst build.py conf.py _templates/layout.html)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
|
||||
DESTINATION share/doc/fmt OPTIONAL
|
||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt OPTIONAL
|
||||
PATTERN ".doctrees" EXCLUDE)
|
||||
|
|
|
@ -15,6 +15,7 @@ The {fmt} library API consists of the following parts:
|
|||
and tuples
|
||||
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
|
||||
* :ref:`fmt/compile.h <compile-api>`: format string compilation
|
||||
* :ref:`fmt/color.h <color-api>`: terminal color and text style
|
||||
* :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support
|
||||
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
|
||||
|
||||
|
@ -47,6 +48,10 @@ participate in an overload resolution if the latter is not a string.
|
|||
.. doxygenfunction:: format(const S&, Args&&...)
|
||||
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<type_identity_t<Char>>>)
|
||||
|
||||
.. doxygenfunction:: fmt::format_to(OutputIt, const S&, Args&&...)
|
||||
.. doxygenfunction:: fmt::format_to_n(OutputIt, size_t, const S&, const Args&...)
|
||||
.. doxygenfunction:: fmt::formatted_size(string_view, Args&&...)
|
||||
|
||||
.. _print:
|
||||
|
||||
.. doxygenfunction:: print(const S&, Args&&...)
|
||||
|
@ -65,6 +70,35 @@ Named arguments are not supported in compile-time checks at the moment.
|
|||
Argument Lists
|
||||
--------------
|
||||
|
||||
You can create your own formatting function with compile-time checks and small
|
||||
binary footprint, for example (https://godbolt.org/z/oba4Mc):
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
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);
|
||||
|
||||
Note that ``vlog`` is not parameterized on argument types which improves compile
|
||||
times and reduces binary code size compared to a fully parameterized version.
|
||||
|
||||
.. doxygenfunction:: fmt::make_args_checked(const S&, const remove_reference_t<Args>&...)
|
||||
|
||||
.. doxygenfunction:: fmt::make_format_args(const Args&...)
|
||||
|
||||
.. doxygenclass:: fmt::format_arg_store
|
||||
|
@ -93,7 +127,7 @@ Compatibility
|
|||
Locale
|
||||
------
|
||||
|
||||
All formatting is locale-independent by default. Use the ``'n'`` format
|
||||
All formatting is locale-independent by default. Use the ``'L'`` format
|
||||
specifier to insert the appropriate number separator characters from the
|
||||
locale::
|
||||
|
||||
|
@ -114,12 +148,14 @@ string checks, wide string, output iterator and user-defined type support.
|
|||
Compile-time Format String Checks
|
||||
---------------------------------
|
||||
|
||||
Compile-time checks are supported for built-in and string types as well as
|
||||
user-defined types with ``constexpr`` ``parse`` functions in their ``formatter``
|
||||
specializations.
|
||||
Compile-time checks are enabled when using ``FMT_STRING``. They support built-in
|
||||
and string types as well as user-defined types with ``constexpr`` ``parse``
|
||||
functions in their ``formatter`` specializations.
|
||||
|
||||
.. doxygendefine:: FMT_STRING
|
||||
|
||||
.. _udt:
|
||||
|
||||
Formatting User-defined Types
|
||||
-----------------------------
|
||||
|
||||
|
@ -237,6 +273,10 @@ You can also write a formatter for a hierarchy of classes::
|
|||
fmt::print("{}", a); // prints "B"
|
||||
}
|
||||
|
||||
If a type provides both a ``formatter`` specialization and an implicit
|
||||
conversion to a formattable type, the specialization takes precedence over the
|
||||
conversion.
|
||||
|
||||
.. doxygenclass:: fmt::basic_format_parse_context
|
||||
:members:
|
||||
|
||||
|
@ -264,7 +304,9 @@ Utilities
|
|||
|
||||
.. doxygentypedef:: fmt::char_t
|
||||
|
||||
.. doxygenfunction:: fmt::formatted_size(string_view, const Args&...)
|
||||
.. doxygenfunction:: fmt::ptr(const T *)
|
||||
.. doxygenfunction:: fmt::ptr(const std::unique_ptr<T>&)
|
||||
.. doxygenfunction:: fmt::ptr(const std::shared_ptr<T>&)
|
||||
|
||||
.. doxygenfunction:: fmt::to_string(const T&)
|
||||
|
||||
|
@ -272,7 +314,7 @@ Utilities
|
|||
|
||||
.. doxygenfunction:: fmt::to_string_view(const Char *)
|
||||
|
||||
.. doxygenfunction:: fmt::join(const Range&, string_view)
|
||||
.. doxygenfunction:: fmt::join(Range&&, string_view)
|
||||
|
||||
.. doxygenfunction:: fmt::join(It, Sentinel, string_view)
|
||||
|
||||
|
@ -298,8 +340,6 @@ the value of ``errno`` being preserved by library functions.
|
|||
.. doxygenclass:: fmt::windows_error
|
||||
:members:
|
||||
|
||||
.. _formatstrings:
|
||||
|
||||
Custom Allocators
|
||||
-----------------
|
||||
|
||||
|
@ -330,10 +370,10 @@ allocator::
|
|||
return vformat(alloc, format_str, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
The allocator will be used for the output container only. If you are using named
|
||||
arguments, the container that stores pointers to them will be allocated using
|
||||
the default allocator. Also floating-point formatting falls back on ``sprintf``
|
||||
which may do allocations.
|
||||
The allocator will be used for the output container only. Formatting functions
|
||||
normally don't do any allocations for built-in and string types except for
|
||||
non-default floating-point formatting that occasionally falls back on
|
||||
``sprintf``.
|
||||
|
||||
.. _ranges-api:
|
||||
|
||||
|
@ -383,9 +423,10 @@ The format string syntax is described in the documentation of
|
|||
Format string compilation
|
||||
=========================
|
||||
|
||||
``fmt/compile.h`` provides format string compilation support. Format strings
|
||||
are parsed at compile time and converted into efficient formatting code. This
|
||||
supports arguments of built-in and string types as well as user-defined types
|
||||
``fmt/compile.h`` provides format string compilation support when using
|
||||
``FMT_COMPILE``. Format strings are parsed, checked and converted
|
||||
into efficient formatting code at compile-time.
|
||||
This supports arguments of built-in and string types as well as user-defined types
|
||||
with ``constexpr`` ``parse`` functions in their ``formatter`` specializations.
|
||||
Format string compilation can generate more binary code compared to the default
|
||||
API and is only recommended in places where formatting is a performance
|
||||
|
@ -393,6 +434,15 @@ bottleneck.
|
|||
|
||||
.. doxygendefine:: FMT_COMPILE
|
||||
|
||||
.. _color-api:
|
||||
|
||||
Terminal color and text style
|
||||
=============================
|
||||
|
||||
``fmt/color.h`` provides support for terminal color and text style output.
|
||||
|
||||
.. doxygenfunction:: print(const text_style&, const S&, const Args&...)
|
||||
|
||||
.. _ostream-api:
|
||||
|
||||
``std::ostream`` Support
|
||||
|
|
|
@ -6,20 +6,10 @@ import errno, os, shutil, sys, tempfile
|
|||
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3']
|
||||
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2', '7.1.3']
|
||||
|
||||
def pip_install(package, commit=None, **kwargs):
|
||||
"Install package using pip."
|
||||
min_version = kwargs.get('min_version')
|
||||
if min_version:
|
||||
from pkg_resources import get_distribution, DistributionNotFound
|
||||
try:
|
||||
installed_version = get_distribution(os.path.basename(package)).version
|
||||
if LooseVersion(installed_version) >= min_version:
|
||||
print('{} {} already installed'.format(package, min_version))
|
||||
return
|
||||
except DistributionNotFound:
|
||||
pass
|
||||
if commit:
|
||||
package = 'git+https://github.com/{0}.git@{1}'.format(package, commit)
|
||||
print('Installing {0}'.format(package))
|
||||
|
@ -52,12 +42,11 @@ def create_build_env(dirname='virtualenv'):
|
|||
check_call(['pip', 'install', '--upgrade', 'distribute'])
|
||||
except DistributionNotFound:
|
||||
pass
|
||||
# Install Sphinx and Breathe.
|
||||
pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee',
|
||||
min_version='1.4.1.dev20160531')
|
||||
# Install Sphinx and Breathe. Require the exact version of Sphinx which is
|
||||
# compatible with Breathe.
|
||||
pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee')
|
||||
pip_install('michaeljones/breathe',
|
||||
'129222318f7c8f865d2631e7da7b033567e7f56a',
|
||||
min_version='4.2.0')
|
||||
'129222318f7c8f865d2631e7da7b033567e7f56a')
|
||||
|
||||
def build_docs(version='dev', **kwargs):
|
||||
doc_dir = kwargs.get('doc_dir', os.path.dirname(os.path.realpath(__file__)))
|
||||
|
@ -74,8 +63,8 @@ def build_docs(version='dev', **kwargs):
|
|||
GENERATE_MAN = NO
|
||||
GENERATE_RTF = NO
|
||||
CASE_SENSE_NAMES = NO
|
||||
INPUT = {0}/core.h {0}/compile.h {0}/format.h {0}/os.h \
|
||||
{0}/ostream.h {0}/printf.h {0}/time.h
|
||||
INPUT = {0}/chrono.h {0}/color.h {0}/core.h {0}/compile.h \
|
||||
{0}/format.h {0}/os.h {0}/ostream.h {0}/printf.h
|
||||
QUIET = YES
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
AUTOLINK_SUPPORT = NO
|
||||
|
@ -95,7 +84,8 @@ def build_docs(version='dev', **kwargs):
|
|||
"FMT_END_NAMESPACE=}}" \
|
||||
"FMT_STRING_ALIAS=1" \
|
||||
"FMT_ENABLE_IF(B)="
|
||||
EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str
|
||||
EXCLUDE_SYMBOLS = fmt::formatter fmt::printf_formatter fmt::arg_join \
|
||||
fmt::basic_format_arg::handle
|
||||
'''.format(include_dir, doxyxml_dir).encode('UTF-8'))
|
||||
if p.returncode != 0:
|
||||
raise CalledProcessError(p.returncode, cmd)
|
||||
|
|
|
@ -15,6 +15,7 @@ The {fmt} library API consists of the following parts:
|
|||
and tuples
|
||||
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
|
||||
* :ref:`fmt/compile.h <compile-api>`: format string compilation
|
||||
* :ref:`fmt/color.h <color-api>`: terminal color and text style
|
||||
* :ref:`fmt/ostream.h <ostream-api>`: ``std::ostream`` support
|
||||
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
|
||||
|
||||
|
@ -47,6 +48,10 @@ participate in an overload resolution if the latter is not a string.
|
|||
.. doxygenfunction:: format(const S&, Args&&...)
|
||||
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<type_identity_t<Char>>>)
|
||||
|
||||
.. doxygenfunction:: fmt::format_to(OutputIt, const S&, Args&&...)
|
||||
.. doxygenfunction:: fmt::format_to_n(OutputIt, size_t, const S&, const Args&...)
|
||||
.. doxygenfunction:: fmt::formatted_size(string_view, Args&&...)
|
||||
|
||||
.. _print:
|
||||
|
||||
.. doxygenfunction:: print(const S&, Args&&...)
|
||||
|
@ -65,6 +70,35 @@ Named arguments are not supported in compile-time checks at the moment.
|
|||
Argument Lists
|
||||
--------------
|
||||
|
||||
You can create your own formatting function with compile-time checks and small
|
||||
binary footprint, for example (https://godbolt.org/z/oba4Mc):
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
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);
|
||||
|
||||
Note that ``vlog`` is not parameterized on argument types which improves compile
|
||||
times and reduces binary code size compared to a fully parameterized version.
|
||||
|
||||
.. doxygenfunction:: fmt::make_args_checked(const S&, const remove_reference_t<Args>&...)
|
||||
|
||||
.. doxygenfunction:: fmt::make_format_args(const Args&...)
|
||||
|
||||
.. doxygenclass:: fmt::format_arg_store
|
||||
|
@ -93,7 +127,7 @@ Compatibility
|
|||
Locale
|
||||
------
|
||||
|
||||
All formatting is locale-independent by default. Use the ``'n'`` format
|
||||
All formatting is locale-independent by default. Use the ``'L'`` format
|
||||
specifier to insert the appropriate number separator characters from the
|
||||
locale::
|
||||
|
||||
|
@ -114,12 +148,14 @@ string checks, wide string, output iterator and user-defined type support.
|
|||
Compile-time Format String Checks
|
||||
---------------------------------
|
||||
|
||||
Compile-time checks are supported for built-in and string types as well as
|
||||
user-defined types with ``constexpr`` ``parse`` functions in their ``formatter``
|
||||
specializations.
|
||||
Compile-time checks are enabled when using ``FMT_STRING``. They support built-in
|
||||
and string types as well as user-defined types with ``constexpr`` ``parse``
|
||||
functions in their ``formatter`` specializations.
|
||||
|
||||
.. doxygendefine:: FMT_STRING
|
||||
|
||||
.. _udt:
|
||||
|
||||
Formatting User-defined Types
|
||||
-----------------------------
|
||||
|
||||
|
@ -237,6 +273,10 @@ You can also write a formatter for a hierarchy of classes::
|
|||
fmt::print("{}", a); // prints "B"
|
||||
}
|
||||
|
||||
If a type provides both a ``formatter`` specialization and an implicit
|
||||
conversion to a formattable type, the specialization takes precedence over the
|
||||
conversion.
|
||||
|
||||
.. doxygenclass:: fmt::basic_format_parse_context
|
||||
:members:
|
||||
|
||||
|
@ -264,7 +304,9 @@ Utilities
|
|||
|
||||
.. doxygentypedef:: fmt::char_t
|
||||
|
||||
.. doxygenfunction:: fmt::formatted_size(string_view, const Args&...)
|
||||
.. doxygenfunction:: fmt::ptr(const T *)
|
||||
.. doxygenfunction:: fmt::ptr(const std::unique_ptr<T>&)
|
||||
.. doxygenfunction:: fmt::ptr(const std::shared_ptr<T>&)
|
||||
|
||||
.. doxygenfunction:: fmt::to_string(const T&)
|
||||
|
||||
|
@ -272,7 +314,7 @@ Utilities
|
|||
|
||||
.. doxygenfunction:: fmt::to_string_view(const Char *)
|
||||
|
||||
.. doxygenfunction:: fmt::join(const Range&, string_view)
|
||||
.. doxygenfunction:: fmt::join(Range&&, string_view)
|
||||
|
||||
.. doxygenfunction:: fmt::join(It, Sentinel, string_view)
|
||||
|
||||
|
@ -298,8 +340,6 @@ the value of ``errno`` being preserved by library functions.
|
|||
.. doxygenclass:: fmt::windows_error
|
||||
:members:
|
||||
|
||||
.. _formatstrings:
|
||||
|
||||
Custom Allocators
|
||||
-----------------
|
||||
|
||||
|
@ -330,10 +370,10 @@ allocator::
|
|||
return vformat(alloc, format_str, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
The allocator will be used for the output container only. If you are using named
|
||||
arguments, the container that stores pointers to them will be allocated using
|
||||
the default allocator. Also floating-point formatting falls back on ``sprintf``
|
||||
which may do allocations.
|
||||
The allocator will be used for the output container only. Formatting functions
|
||||
normally don't do any allocations for built-in and string types except for
|
||||
non-default floating-point formatting that occasionally falls back on
|
||||
``sprintf``.
|
||||
|
||||
.. _ranges-api:
|
||||
|
||||
|
@ -383,9 +423,10 @@ The format string syntax is described in the documentation of
|
|||
Format string compilation
|
||||
=========================
|
||||
|
||||
``fmt/compile.h`` provides format string compilation support. Format strings
|
||||
are parsed at compile time and converted into efficient formatting code. This
|
||||
supports arguments of built-in and string types as well as user-defined types
|
||||
``fmt/compile.h`` provides format string compilation support when using
|
||||
``FMT_COMPILE``. Format strings are parsed, checked and converted
|
||||
into efficient formatting code at compile-time.
|
||||
This supports arguments of built-in and string types as well as user-defined types
|
||||
with ``constexpr`` ``parse`` functions in their ``formatter`` specializations.
|
||||
Format string compilation can generate more binary code compared to the default
|
||||
API and is only recommended in places where formatting is a performance
|
||||
|
@ -393,6 +434,15 @@ bottleneck.
|
|||
|
||||
.. doxygendefine:: FMT_COMPILE
|
||||
|
||||
.. _color-api:
|
||||
|
||||
Terminal color and text style
|
||||
=============================
|
||||
|
||||
``fmt/color.h`` provides support for terminal color and text style output.
|
||||
|
||||
.. doxygenfunction:: print(const text_style&, const S&, const Args&...)
|
||||
|
||||
.. _ostream-api:
|
||||
|
||||
``std::ostream`` Support
|
|
@ -23,7 +23,7 @@ Format API
|
|||
|
||||
The format API is similar in spirit to the C ``printf`` family of function but
|
||||
is safer, simpler and several times `faster
|
||||
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
|
||||
<https://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
|
||||
than common standard library implementations.
|
||||
The `format string syntax <syntax.html>`_ is similar to the one used by
|
||||
`str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_ in
|
||||
|
@ -48,21 +48,19 @@ The ``fmt::print`` function performs formatting and writes the result to a strea
|
|||
|
||||
fmt::print(stderr, "System error code = {}\n", errno);
|
||||
|
||||
The file argument can be omitted in which case the function prints to
|
||||
``stdout``:
|
||||
If you omit the file argument the function will print to ``stdout``:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
fmt::print("Don't {}\n", "panic");
|
||||
|
||||
The Format API also supports positional arguments useful for localization:
|
||||
The format API also supports positional arguments useful for localization:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
fmt::print("I'd rather be {1} than {0}.", "right", "happy");
|
||||
|
||||
Named arguments can be created with ``fmt::arg``. This makes it easier to track
|
||||
what goes where when multiple arguments are being formatted:
|
||||
You can pass named arguments with ``fmt::arg``:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
|
@ -91,16 +89,17 @@ time. For example, the code
|
|||
|
||||
fmt::format("The answer is {:d}", "forty-two");
|
||||
|
||||
throws a ``format_error`` exception with description "unknown format code 'd' for
|
||||
string", because the argument ``"forty-two"`` is a string while the format code
|
||||
``d`` only applies to integers, while
|
||||
throws the ``format_error`` exception because the argument ``"forty-two"`` is a
|
||||
string while the format code ``d`` only applies to integers.
|
||||
|
||||
The code
|
||||
|
||||
.. code:: c++
|
||||
|
||||
format(FMT_STRING("The answer is {:d}"), "forty-two");
|
||||
|
||||
reports a compile-time error for the same reason on compilers that support
|
||||
relaxed ``constexpr``. See `here <api.html#c.fmt>`_ for details.
|
||||
reports a compile-time error on compilers that support relaxed ``constexpr``.
|
||||
See `here <api.html#c.fmt>`_ for details.
|
||||
|
||||
The following code
|
||||
|
||||
|
@ -117,13 +116,13 @@ formatted into a narrow string. You can use a wide format string instead:
|
|||
|
||||
For comparison, writing a wide character to ``std::ostream`` results in
|
||||
its numeric value being written to the stream (i.e. 1070 instead of letter 'ю'
|
||||
which is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is
|
||||
needed.
|
||||
which is represented by ``L'\x42e'`` if we use Unicode) which is rarely
|
||||
desirable.
|
||||
|
||||
Compact Binary Code
|
||||
-------------------
|
||||
|
||||
The library is designed to produce compact per-call compiled code. For example
|
||||
The library produces compact per-call compiled code. For example
|
||||
(`godbolt <https://godbolt.org/g/TZU4KF>`_),
|
||||
|
||||
.. code:: c++
|
||||
|
@ -144,8 +143,8 @@ compiles to just
|
|||
mov rcx, rsp
|
||||
mov edi, offset .L.str
|
||||
mov esi, 17
|
||||
mov edx, 2
|
||||
call fmt::v5::vprint(fmt::v5::basic_string_view<char>, fmt::v5::format_args)
|
||||
mov edx, 1
|
||||
call fmt::v7::vprint(fmt::v7::basic_string_view<char>, fmt::v7::format_args)
|
||||
xor eax, eax
|
||||
add rsp, 24
|
||||
ret
|
||||
|
@ -167,20 +166,19 @@ The library is highly portable and relies only on a small set of C++11 features:
|
|||
* deleted functions
|
||||
* alias templates
|
||||
|
||||
These are available since GCC 4.8, Clang 3.0 and MSVC 19.0 (2015). For older
|
||||
compilers use {fmt} `version 4.x
|
||||
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
|
||||
maintained and only requires C++98.
|
||||
These are available in GCC 4.8, Clang 3.0, MSVC 19.0 (2015) and more recent
|
||||
compiler version. For older compilers use {fmt} `version 4.x
|
||||
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which is maintained and
|
||||
only requires C++98.
|
||||
|
||||
The output of all formatting functions is consistent across platforms. In
|
||||
particular, formatting a floating-point infinity always gives ``inf`` while the
|
||||
output of ``printf`` is platform-dependent. For example,
|
||||
The output of all formatting functions is consistent across platforms.
|
||||
For example,
|
||||
|
||||
.. code::
|
||||
|
||||
fmt::print("{}", std::numeric_limits<double>::infinity());
|
||||
|
||||
always prints ``inf``.
|
||||
always prints ``inf`` while the output of ``printf`` is platform-dependent.
|
||||
|
||||
.. _ease-of-use:
|
||||
|
|
@ -52,6 +52,14 @@ To build a `shared library`__ set the ``BUILD_SHARED_LIBS`` CMake variable to
|
|||
|
||||
__ http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries
|
||||
|
||||
|
||||
To build a `static library` with position independent code (required if the main
|
||||
consumer of the fmt library is a shared library i.e. a Python extension) set the
|
||||
``CMAKE_POSITION_INDEPENDENT_CODE`` CMake variable to ``TRUE``::
|
||||
|
||||
cmake -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE ...
|
||||
|
||||
|
||||
Installing the Library
|
||||
======================
|
||||
|
||||
|
@ -83,6 +91,49 @@ Setting up your target to use a header-only version of ``fmt`` is equally easy::
|
|||
|
||||
target_link_libraries(<your-target> PRIVATE fmt::fmt-header-only)
|
||||
|
||||
Usage with build2
|
||||
=================
|
||||
|
||||
You can use `build2 <https://build2.org>`_, a dependency manager and a
|
||||
build-system combined, to use ``fmt``.
|
||||
|
||||
Currently this package is available in these package repositories:
|
||||
|
||||
- **https://cppget.org/fmt/** for released and published versions.
|
||||
- `The git repository with the sources of the build2 package of fmt <https://github.com/build2-packaging/fmt.git>`_
|
||||
for unreleased or custom revisions of ``fmt``.
|
||||
|
||||
**Usage:**
|
||||
|
||||
- ``build2`` package name: ``fmt``
|
||||
- Library target name : ``lib{fmt}``
|
||||
|
||||
For example, to make your ``build2`` project depend on ``fmt``:
|
||||
|
||||
- Add one of the repositories to your configurations, or in your
|
||||
``repositories.manifest``, if not already there::
|
||||
|
||||
:
|
||||
role: prerequisite
|
||||
location: https://pkg.cppget.org/1/stable
|
||||
|
||||
- Add this package as a dependency to your ``./manifest`` file
|
||||
(example for ``v7.0.x``)::
|
||||
|
||||
depends: fmt ~7.0.0
|
||||
|
||||
- Import the target and use it as a prerequisite to your own target
|
||||
using `fmt` in the appropriate ``buildfile``::
|
||||
|
||||
import fmt = fmt%lib{fmt}
|
||||
lib{mylib} : cxx{**} ... $fmt
|
||||
|
||||
Then build your project as usual with `b` or `bdep update`.
|
||||
|
||||
For ``build2`` newcomers or to get more details and use cases, you can read the
|
||||
``build2``
|
||||
`toolchain introduction <https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml>`_.
|
||||
|
||||
Building the Documentation
|
||||
==========================
|
||||
|
||||
|
@ -130,6 +181,18 @@ The fmt port in vcpkg is kept up to date by Microsoft team members and community
|
|||
contributors. If the version is out of date, please `create an issue or pull
|
||||
request <https://github.com/Microsoft/vcpkg>`__ on the vcpkg repository.
|
||||
|
||||
LHelper
|
||||
=======
|
||||
|
||||
You can download and install fmt using
|
||||
`lhelper <https://github.com/franko/lhelper>`__ dependency manager::
|
||||
|
||||
lhelper activate <some-environment>
|
||||
lhelper install fmt
|
||||
|
||||
All the recipes for lhelper are kept in the
|
||||
`lhelper's recipe <https://github.com/franko/lhelper-recipes>`__ repository.
|
||||
|
||||
Android NDK
|
||||
===========
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* Sphinx stylesheet -- basic theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
@ -81,26 +81,14 @@ div.sphinxsidebar input {
|
|||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox form.search {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox input[type="text"] {
|
||||
float: left;
|
||||
width: 80%;
|
||||
padding: 0.25em;
|
||||
box-sizing: border-box;
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar #searchbox input[type="submit"] {
|
||||
float: left;
|
||||
width: 20%;
|
||||
border-left: none;
|
||||
padding: 0.25em;
|
||||
box-sizing: border-box;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
max-width: 100%;
|
||||
|
@ -138,8 +126,6 @@ ul.keywordmatches li.goodmatch a {
|
|||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
|
@ -167,14 +153,9 @@ table.indextable td {
|
|||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable ul {
|
||||
table.indextable dl, table.indextable dd {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
table.indextable > tbody > tr > td > ul {
|
||||
padding-left: 0em;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
|
@ -206,20 +187,8 @@ div.genindex-jumpbox {
|
|||
padding: 0.4em;
|
||||
}
|
||||
|
||||
/* -- domain module index --------------------------------------------------- */
|
||||
|
||||
table.modindextable td {
|
||||
padding: 2px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/* -- general body styles --------------------------------------------------- */
|
||||
|
||||
div.body {
|
||||
min-width: 450px;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li, div.body blockquote {
|
||||
-moz-hyphens: auto;
|
||||
-ms-hyphens: auto;
|
||||
|
@ -252,6 +221,10 @@ div.body td {
|
|||
text-align: left;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
@ -353,11 +326,6 @@ table.docutils {
|
|||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table.align-center {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
table caption span.caption-number {
|
||||
font-style: italic;
|
||||
}
|
||||
|
@ -373,6 +341,10 @@ table.docutils td, table.docutils th {
|
|||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
table.field-list td, table.field-list th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
table.footnote td, table.footnote th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
@ -409,34 +381,6 @@ div.figure p.caption span.caption-number {
|
|||
div.figure p.caption span.caption-text {
|
||||
}
|
||||
|
||||
/* -- field list styles ----------------------------------------------------- */
|
||||
|
||||
table.field-list td, table.field-list th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.field-list p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.field-name {
|
||||
-moz-hyphens: manual;
|
||||
-ms-hyphens: manual;
|
||||
-webkit-hyphens: manual;
|
||||
hyphens: manual;
|
||||
}
|
||||
|
||||
/* -- hlist styles ---------------------------------------------------------- */
|
||||
|
||||
table.hlist td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
|
||||
/* -- other body styles ----------------------------------------------------- */
|
||||
|
||||
|
@ -478,19 +422,24 @@ dd {
|
|||
margin-left: 30px;
|
||||
}
|
||||
|
||||
dt:target, span.highlighted {
|
||||
dt:target, .highlighted {
|
||||
background-color: #fbe54e;
|
||||
}
|
||||
|
||||
rect.highlighted {
|
||||
fill: #fbe54e;
|
||||
}
|
||||
|
||||
dl.glossary dt {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.field-list p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.optional {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
@ -549,13 +498,6 @@ pre {
|
|||
overflow-y: hidden; /* fixes display issues on Chrome browsers */
|
||||
}
|
||||
|
||||
span.pre {
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
-webkit-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
td.linenos pre {
|
||||
padding: 5px 0px;
|
||||
border: 0;
|
||||
|
@ -647,16 +589,6 @@ span.eqno {
|
|||
float: right;
|
||||
}
|
||||
|
||||
span.eqno a.headerlink {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
div.math:hover a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* -- printout stylesheet --------------------------------------------------- */
|
||||
|
||||
@media print {
|
||||
|
|
Before Width: | Height: | Size: 756 B After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 829 B After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 641 B After Width: | Height: | Size: 3.4 KiB |
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* Sphinx JavaScript utilities for all documentation.
|
||||
*
|
||||
* :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
@ -45,7 +45,7 @@ jQuery.urlencode = encodeURIComponent;
|
|||
* it will always return arrays of strings for the value parts.
|
||||
*/
|
||||
jQuery.getQueryParameters = function(s) {
|
||||
if (typeof s === 'undefined')
|
||||
if (typeof s == 'undefined')
|
||||
s = document.location.search;
|
||||
var parts = s.substr(s.indexOf('?') + 1).split('&');
|
||||
var result = {};
|
||||
|
@ -66,55 +66,29 @@ jQuery.getQueryParameters = function(s) {
|
|||
* span elements with the given class name.
|
||||
*/
|
||||
jQuery.fn.highlightText = function(text, className) {
|
||||
function highlight(node, addItems) {
|
||||
if (node.nodeType === 3) {
|
||||
function highlight(node) {
|
||||
if (node.nodeType == 3) {
|
||||
var val = node.nodeValue;
|
||||
var pos = val.toLowerCase().indexOf(text);
|
||||
if (pos >= 0 &&
|
||||
!jQuery(node.parentNode).hasClass(className) &&
|
||||
!jQuery(node.parentNode).hasClass("nohighlight")) {
|
||||
var span;
|
||||
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
|
||||
if (isInSVG) {
|
||||
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
|
||||
} else {
|
||||
span = document.createElement("span");
|
||||
span.className = className;
|
||||
}
|
||||
if (pos >= 0 && !jQuery(node.parentNode).hasClass(className)) {
|
||||
var span = document.createElement("span");
|
||||
span.className = className;
|
||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
||||
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
|
||||
document.createTextNode(val.substr(pos + text.length)),
|
||||
node.nextSibling));
|
||||
node.nodeValue = val.substr(0, pos);
|
||||
if (isInSVG) {
|
||||
var bbox = span.getBBox();
|
||||
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
||||
rect.x.baseVal.value = bbox.x;
|
||||
rect.y.baseVal.value = bbox.y;
|
||||
rect.width.baseVal.value = bbox.width;
|
||||
rect.height.baseVal.value = bbox.height;
|
||||
rect.setAttribute('class', className);
|
||||
var parentOfText = node.parentNode.parentNode;
|
||||
addItems.push({
|
||||
"parent": node.parentNode,
|
||||
"target": rect});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!jQuery(node).is("button, select, textarea")) {
|
||||
jQuery.each(node.childNodes, function() {
|
||||
highlight(this, addItems);
|
||||
highlight(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
var addItems = [];
|
||||
var result = this.each(function() {
|
||||
highlight(this, addItems);
|
||||
return this.each(function() {
|
||||
highlight(this);
|
||||
});
|
||||
for (var i = 0; i < addItems.length; ++i) {
|
||||
jQuery(addItems[i].parent).before(addItems[i].target);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -150,30 +124,28 @@ var Documentation = {
|
|||
this.fixFirefoxAnchorBug();
|
||||
this.highlightSearchWords();
|
||||
this.initIndexTable();
|
||||
if (DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) {
|
||||
this.initOnKeyListeners();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* i18n support
|
||||
*/
|
||||
TRANSLATIONS : {},
|
||||
PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; },
|
||||
PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; },
|
||||
LOCALE : 'unknown',
|
||||
|
||||
// gettext and ngettext don't access this so that the functions
|
||||
// can safely bound to a different name (_ = Documentation.gettext)
|
||||
gettext : function(string) {
|
||||
var translated = Documentation.TRANSLATIONS[string];
|
||||
if (typeof translated === 'undefined')
|
||||
if (typeof translated == 'undefined')
|
||||
return string;
|
||||
return (typeof translated === 'string') ? translated : translated[0];
|
||||
return (typeof translated == 'string') ? translated : translated[0];
|
||||
},
|
||||
|
||||
ngettext : function(singular, plural, n) {
|
||||
var translated = Documentation.TRANSLATIONS[singular];
|
||||
if (typeof translated === 'undefined')
|
||||
if (typeof translated == 'undefined')
|
||||
return (n == 1) ? singular : plural;
|
||||
return translated[Documentation.PLURALEXPR(n)];
|
||||
},
|
||||
|
@ -208,7 +180,7 @@ var Documentation = {
|
|||
* see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
|
||||
*/
|
||||
fixFirefoxAnchorBug : function() {
|
||||
if (document.location.hash && $.browser.mozilla)
|
||||
if (document.location.hash)
|
||||
window.setTimeout(function() {
|
||||
document.location.href += '';
|
||||
}, 10);
|
||||
|
@ -244,7 +216,7 @@ var Documentation = {
|
|||
var src = $(this).attr('src');
|
||||
var idnum = $(this).attr('id').substr(7);
|
||||
$('tr.cg-' + idnum).toggle();
|
||||
if (src.substr(-9) === 'minus.png')
|
||||
if (src.substr(-9) == 'minus.png')
|
||||
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
|
||||
else
|
||||
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
|
||||
|
@ -276,7 +248,7 @@ var Documentation = {
|
|||
var path = document.location.pathname;
|
||||
var parts = path.split(/\//);
|
||||
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
|
||||
if (this === '..')
|
||||
if (this == '..')
|
||||
parts.pop();
|
||||
});
|
||||
var url = parts.join('/');
|
||||
|
@ -312,4 +284,4 @@ _ = Documentation.gettext;
|
|||
|
||||
$(document).ready(function() {
|
||||
Documentation.init();
|
||||
});
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '7.0.3',
|
||||
LANGUAGE: 'None',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt',
|
||||
NAVIGATION_WITH_KEYS: false,
|
||||
};
|
Before Width: | Height: | Size: 222 B After Width: | Height: | Size: 347 B |
Before Width: | Height: | Size: 202 B After Width: | Height: | Size: 347 B |
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 358 B |
8
fmt/doc/html/_static/jquery.js
vendored
|
@ -1,297 +0,0 @@
|
|||
/*
|
||||
* language_data.js
|
||||
* ~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This script contains the language-specific data used by searchtools.js,
|
||||
* namely the list of stopwords, stemmer, scorer and splitter.
|
||||
*
|
||||
* :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
|
||||
|
||||
|
||||
/* Non-minified version JS is _stemmer.js if file is provided */
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
var Stemmer = function() {
|
||||
|
||||
var step2list = {
|
||||
ational: 'ate',
|
||||
tional: 'tion',
|
||||
enci: 'ence',
|
||||
anci: 'ance',
|
||||
izer: 'ize',
|
||||
bli: 'ble',
|
||||
alli: 'al',
|
||||
entli: 'ent',
|
||||
eli: 'e',
|
||||
ousli: 'ous',
|
||||
ization: 'ize',
|
||||
ation: 'ate',
|
||||
ator: 'ate',
|
||||
alism: 'al',
|
||||
iveness: 'ive',
|
||||
fulness: 'ful',
|
||||
ousness: 'ous',
|
||||
aliti: 'al',
|
||||
iviti: 'ive',
|
||||
biliti: 'ble',
|
||||
logi: 'log'
|
||||
};
|
||||
|
||||
var step3list = {
|
||||
icate: 'ic',
|
||||
ative: '',
|
||||
alize: 'al',
|
||||
iciti: 'ic',
|
||||
ical: 'ic',
|
||||
ful: '',
|
||||
ness: ''
|
||||
};
|
||||
|
||||
var c = "[^aeiou]"; // consonant
|
||||
var v = "[aeiouy]"; // vowel
|
||||
var C = c + "[^aeiouy]*"; // consonant sequence
|
||||
var V = v + "[aeiou]*"; // vowel sequence
|
||||
|
||||
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
|
||||
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
|
||||
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
|
||||
var s_v = "^(" + C + ")?" + v; // vowel in stem
|
||||
|
||||
this.stemWord = function (w) {
|
||||
var stem;
|
||||
var suffix;
|
||||
var firstch;
|
||||
var origword = w;
|
||||
|
||||
if (w.length < 3)
|
||||
return w;
|
||||
|
||||
var re;
|
||||
var re2;
|
||||
var re3;
|
||||
var re4;
|
||||
|
||||
firstch = w.substr(0,1);
|
||||
if (firstch == "y")
|
||||
w = firstch.toUpperCase() + w.substr(1);
|
||||
|
||||
// Step 1a
|
||||
re = /^(.+?)(ss|i)es$/;
|
||||
re2 = /^(.+?)([^s])s$/;
|
||||
|
||||
if (re.test(w))
|
||||
w = w.replace(re,"$1$2");
|
||||
else if (re2.test(w))
|
||||
w = w.replace(re2,"$1$2");
|
||||
|
||||
// Step 1b
|
||||
re = /^(.+?)eed$/;
|
||||
re2 = /^(.+?)(ed|ing)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(fp[1])) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1];
|
||||
re2 = new RegExp(s_v);
|
||||
if (re2.test(stem)) {
|
||||
w = stem;
|
||||
re2 = /(at|bl|iz)$/;
|
||||
re3 = new RegExp("([^aeiouylsz])\\1$");
|
||||
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re2.test(w))
|
||||
w = w + "e";
|
||||
else if (re3.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
else if (re4.test(w))
|
||||
w = w + "e";
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1c
|
||||
re = /^(.+?)y$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(s_v);
|
||||
if (re.test(stem))
|
||||
w = stem + "i";
|
||||
}
|
||||
|
||||
// Step 2
|
||||
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step2list[suffix];
|
||||
}
|
||||
|
||||
// Step 3
|
||||
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step3list[suffix];
|
||||
}
|
||||
|
||||
// Step 4
|
||||
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
||||
re2 = /^(.+?)(s|t)(ion)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
if (re.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1] + fp[2];
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re2.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
|
||||
// Step 5
|
||||
re = /^(.+?)e$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
re2 = new RegExp(meq1);
|
||||
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
|
||||
w = stem;
|
||||
}
|
||||
re = /ll$/;
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re.test(w) && re2.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
|
||||
// and turn initial Y back to y
|
||||
if (firstch == "y")
|
||||
w = firstch.toLowerCase() + w.substr(1);
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var splitChars = (function() {
|
||||
var result = {};
|
||||
var singles = [96, 180, 187, 191, 215, 247, 749, 885, 903, 907, 909, 930, 1014, 1648,
|
||||
1748, 1809, 2416, 2473, 2481, 2526, 2601, 2609, 2612, 2615, 2653, 2702,
|
||||
2706, 2729, 2737, 2740, 2857, 2865, 2868, 2910, 2928, 2948, 2961, 2971,
|
||||
2973, 3085, 3089, 3113, 3124, 3213, 3217, 3241, 3252, 3295, 3341, 3345,
|
||||
3369, 3506, 3516, 3633, 3715, 3721, 3736, 3744, 3748, 3750, 3756, 3761,
|
||||
3781, 3912, 4239, 4347, 4681, 4695, 4697, 4745, 4785, 4799, 4801, 4823,
|
||||
4881, 5760, 5901, 5997, 6313, 7405, 8024, 8026, 8028, 8030, 8117, 8125,
|
||||
8133, 8181, 8468, 8485, 8487, 8489, 8494, 8527, 11311, 11359, 11687, 11695,
|
||||
11703, 11711, 11719, 11727, 11735, 12448, 12539, 43010, 43014, 43019, 43587,
|
||||
43696, 43713, 64286, 64297, 64311, 64317, 64319, 64322, 64325, 65141];
|
||||
var i, j, start, end;
|
||||
for (i = 0; i < singles.length; i++) {
|
||||
result[singles[i]] = true;
|
||||
}
|
||||
var ranges = [[0, 47], [58, 64], [91, 94], [123, 169], [171, 177], [182, 184], [706, 709],
|
||||
[722, 735], [741, 747], [751, 879], [888, 889], [894, 901], [1154, 1161],
|
||||
[1318, 1328], [1367, 1368], [1370, 1376], [1416, 1487], [1515, 1519], [1523, 1568],
|
||||
[1611, 1631], [1642, 1645], [1750, 1764], [1767, 1773], [1789, 1790], [1792, 1807],
|
||||
[1840, 1868], [1958, 1968], [1970, 1983], [2027, 2035], [2038, 2041], [2043, 2047],
|
||||
[2070, 2073], [2075, 2083], [2085, 2087], [2089, 2307], [2362, 2364], [2366, 2383],
|
||||
[2385, 2391], [2402, 2405], [2419, 2424], [2432, 2436], [2445, 2446], [2449, 2450],
|
||||
[2483, 2485], [2490, 2492], [2494, 2509], [2511, 2523], [2530, 2533], [2546, 2547],
|
||||
[2554, 2564], [2571, 2574], [2577, 2578], [2618, 2648], [2655, 2661], [2672, 2673],
|
||||
[2677, 2692], [2746, 2748], [2750, 2767], [2769, 2783], [2786, 2789], [2800, 2820],
|
||||
[2829, 2830], [2833, 2834], [2874, 2876], [2878, 2907], [2914, 2917], [2930, 2946],
|
||||
[2955, 2957], [2966, 2968], [2976, 2978], [2981, 2983], [2987, 2989], [3002, 3023],
|
||||
[3025, 3045], [3059, 3076], [3130, 3132], [3134, 3159], [3162, 3167], [3170, 3173],
|
||||
[3184, 3191], [3199, 3204], [3258, 3260], [3262, 3293], [3298, 3301], [3312, 3332],
|
||||
[3386, 3388], [3390, 3423], [3426, 3429], [3446, 3449], [3456, 3460], [3479, 3481],
|
||||
[3518, 3519], [3527, 3584], [3636, 3647], [3655, 3663], [3674, 3712], [3717, 3718],
|
||||
[3723, 3724], [3726, 3731], [3752, 3753], [3764, 3772], [3774, 3775], [3783, 3791],
|
||||
[3802, 3803], [3806, 3839], [3841, 3871], [3892, 3903], [3949, 3975], [3980, 4095],
|
||||
[4139, 4158], [4170, 4175], [4182, 4185], [4190, 4192], [4194, 4196], [4199, 4205],
|
||||
[4209, 4212], [4226, 4237], [4250, 4255], [4294, 4303], [4349, 4351], [4686, 4687],
|
||||
[4702, 4703], [4750, 4751], [4790, 4791], [4806, 4807], [4886, 4887], [4955, 4968],
|
||||
[4989, 4991], [5008, 5023], [5109, 5120], [5741, 5742], [5787, 5791], [5867, 5869],
|
||||
[5873, 5887], [5906, 5919], [5938, 5951], [5970, 5983], [6001, 6015], [6068, 6102],
|
||||
[6104, 6107], [6109, 6111], [6122, 6127], [6138, 6159], [6170, 6175], [6264, 6271],
|
||||
[6315, 6319], [6390, 6399], [6429, 6469], [6510, 6511], [6517, 6527], [6572, 6592],
|
||||
[6600, 6607], [6619, 6655], [6679, 6687], [6741, 6783], [6794, 6799], [6810, 6822],
|
||||
[6824, 6916], [6964, 6980], [6988, 6991], [7002, 7042], [7073, 7085], [7098, 7167],
|
||||
[7204, 7231], [7242, 7244], [7294, 7400], [7410, 7423], [7616, 7679], [7958, 7959],
|
||||
[7966, 7967], [8006, 8007], [8014, 8015], [8062, 8063], [8127, 8129], [8141, 8143],
|
||||
[8148, 8149], [8156, 8159], [8173, 8177], [8189, 8303], [8306, 8307], [8314, 8318],
|
||||
[8330, 8335], [8341, 8449], [8451, 8454], [8456, 8457], [8470, 8472], [8478, 8483],
|
||||
[8506, 8507], [8512, 8516], [8522, 8525], [8586, 9311], [9372, 9449], [9472, 10101],
|
||||
[10132, 11263], [11493, 11498], [11503, 11516], [11518, 11519], [11558, 11567],
|
||||
[11622, 11630], [11632, 11647], [11671, 11679], [11743, 11822], [11824, 12292],
|
||||
[12296, 12320], [12330, 12336], [12342, 12343], [12349, 12352], [12439, 12444],
|
||||
[12544, 12548], [12590, 12592], [12687, 12689], [12694, 12703], [12728, 12783],
|
||||
[12800, 12831], [12842, 12880], [12896, 12927], [12938, 12976], [12992, 13311],
|
||||
[19894, 19967], [40908, 40959], [42125, 42191], [42238, 42239], [42509, 42511],
|
||||
[42540, 42559], [42592, 42593], [42607, 42622], [42648, 42655], [42736, 42774],
|
||||
[42784, 42785], [42889, 42890], [42893, 43002], [43043, 43055], [43062, 43071],
|
||||
[43124, 43137], [43188, 43215], [43226, 43249], [43256, 43258], [43260, 43263],
|
||||
[43302, 43311], [43335, 43359], [43389, 43395], [43443, 43470], [43482, 43519],
|
||||
[43561, 43583], [43596, 43599], [43610, 43615], [43639, 43641], [43643, 43647],
|
||||
[43698, 43700], [43703, 43704], [43710, 43711], [43715, 43738], [43742, 43967],
|
||||
[44003, 44015], [44026, 44031], [55204, 55215], [55239, 55242], [55292, 55295],
|
||||
[57344, 63743], [64046, 64047], [64110, 64111], [64218, 64255], [64263, 64274],
|
||||
[64280, 64284], [64434, 64466], [64830, 64847], [64912, 64913], [64968, 65007],
|
||||
[65020, 65135], [65277, 65295], [65306, 65312], [65339, 65344], [65371, 65381],
|
||||
[65471, 65473], [65480, 65481], [65488, 65489], [65496, 65497]];
|
||||
for (i = 0; i < ranges.length; i++) {
|
||||
start = ranges[i][0];
|
||||
end = ranges[i][1];
|
||||
for (j = start; j <= end; j++) {
|
||||
result[j] = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
})();
|
||||
|
||||
function splitQuery(query) {
|
||||
var result = [];
|
||||
var start = -1;
|
||||
for (var i = 0; i < query.length; i++) {
|
||||
if (splitChars[query.charCodeAt(i)]) {
|
||||
if (start !== -1) {
|
||||
result.push(query.slice(start, i));
|
||||
start = -1;
|
||||
}
|
||||
} else if (start === -1) {
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
if (start !== -1) {
|
||||
result.push(query.slice(start));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 90 B After Width: | Height: | Size: 173 B |
Before Width: | Height: | Size: 90 B After Width: | Height: | Size: 173 B |
|
@ -1,52 +1,231 @@
|
|||
/*
|
||||
* searchtools.js
|
||||
* searchtools.js_t
|
||||
* ~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx JavaScript utilities for the full-text search.
|
||||
*
|
||||
* :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
if (!Scorer) {
|
||||
/**
|
||||
* Simple result scoring code.
|
||||
*/
|
||||
var Scorer = {
|
||||
// Implement the following function to further tweak the score for each result
|
||||
// The function takes a result array [filename, title, anchor, descr, score]
|
||||
// and returns the new score.
|
||||
/*
|
||||
score: function(result) {
|
||||
return result[4];
|
||||
},
|
||||
*/
|
||||
|
||||
// query matches the full name of an object
|
||||
objNameMatch: 11,
|
||||
// or matches in the last dotted part of the object name
|
||||
objPartialMatch: 6,
|
||||
// Additive scores depending on the priority of the object
|
||||
objPrio: {0: 15, // used to be importantResults
|
||||
1: 5, // used to be objectResults
|
||||
2: -5}, // used to be unimportantResults
|
||||
// Used when the priority is not in the mapping.
|
||||
objPrioDefault: 0,
|
||||
/* Non-minified version JS is _stemmer.js if file is provided */
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
var Stemmer = function() {
|
||||
|
||||
// query found in title
|
||||
title: 15,
|
||||
// query found in terms
|
||||
term: 5
|
||||
var step2list = {
|
||||
ational: 'ate',
|
||||
tional: 'tion',
|
||||
enci: 'ence',
|
||||
anci: 'ance',
|
||||
izer: 'ize',
|
||||
bli: 'ble',
|
||||
alli: 'al',
|
||||
entli: 'ent',
|
||||
eli: 'e',
|
||||
ousli: 'ous',
|
||||
ization: 'ize',
|
||||
ation: 'ate',
|
||||
ator: 'ate',
|
||||
alism: 'al',
|
||||
iveness: 'ive',
|
||||
fulness: 'ful',
|
||||
ousness: 'ous',
|
||||
aliti: 'al',
|
||||
iviti: 'ive',
|
||||
biliti: 'ble',
|
||||
logi: 'log'
|
||||
};
|
||||
}
|
||||
|
||||
if (!splitQuery) {
|
||||
function splitQuery(query) {
|
||||
return query.split(/\s+/);
|
||||
var step3list = {
|
||||
icate: 'ic',
|
||||
ative: '',
|
||||
alize: 'al',
|
||||
iciti: 'ic',
|
||||
ical: 'ic',
|
||||
ful: '',
|
||||
ness: ''
|
||||
};
|
||||
|
||||
var c = "[^aeiou]"; // consonant
|
||||
var v = "[aeiouy]"; // vowel
|
||||
var C = c + "[^aeiouy]*"; // consonant sequence
|
||||
var V = v + "[aeiou]*"; // vowel sequence
|
||||
|
||||
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
|
||||
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
|
||||
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
|
||||
var s_v = "^(" + C + ")?" + v; // vowel in stem
|
||||
|
||||
this.stemWord = function (w) {
|
||||
var stem;
|
||||
var suffix;
|
||||
var firstch;
|
||||
var origword = w;
|
||||
|
||||
if (w.length < 3)
|
||||
return w;
|
||||
|
||||
var re;
|
||||
var re2;
|
||||
var re3;
|
||||
var re4;
|
||||
|
||||
firstch = w.substr(0,1);
|
||||
if (firstch == "y")
|
||||
w = firstch.toUpperCase() + w.substr(1);
|
||||
|
||||
// Step 1a
|
||||
re = /^(.+?)(ss|i)es$/;
|
||||
re2 = /^(.+?)([^s])s$/;
|
||||
|
||||
if (re.test(w))
|
||||
w = w.replace(re,"$1$2");
|
||||
else if (re2.test(w))
|
||||
w = w.replace(re2,"$1$2");
|
||||
|
||||
// Step 1b
|
||||
re = /^(.+?)eed$/;
|
||||
re2 = /^(.+?)(ed|ing)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(fp[1])) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1];
|
||||
re2 = new RegExp(s_v);
|
||||
if (re2.test(stem)) {
|
||||
w = stem;
|
||||
re2 = /(at|bl|iz)$/;
|
||||
re3 = new RegExp("([^aeiouylsz])\\1$");
|
||||
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re2.test(w))
|
||||
w = w + "e";
|
||||
else if (re3.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
else if (re4.test(w))
|
||||
w = w + "e";
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1c
|
||||
re = /^(.+?)y$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(s_v);
|
||||
if (re.test(stem))
|
||||
w = stem + "i";
|
||||
}
|
||||
|
||||
// Step 2
|
||||
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step2list[suffix];
|
||||
}
|
||||
|
||||
// Step 3
|
||||
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem))
|
||||
w = stem + step3list[suffix];
|
||||
}
|
||||
|
||||
// Step 4
|
||||
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
||||
re2 = /^(.+?)(s|t)(ion)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
if (re.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1] + fp[2];
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re2.test(stem))
|
||||
w = stem;
|
||||
}
|
||||
|
||||
// Step 5
|
||||
re = /^(.+?)e$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
re2 = new RegExp(meq1);
|
||||
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
|
||||
w = stem;
|
||||
}
|
||||
re = /ll$/;
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re.test(w) && re2.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
|
||||
// and turn initial Y back to y
|
||||
if (firstch == "y")
|
||||
w = firstch.toLowerCase() + w.substr(1);
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Simple result scoring code.
|
||||
*/
|
||||
var Scorer = {
|
||||
// Implement the following function to further tweak the score for each result
|
||||
// The function takes a result array [filename, title, anchor, descr, score]
|
||||
// and returns the new score.
|
||||
/*
|
||||
score: function(result) {
|
||||
return result[4];
|
||||
},
|
||||
*/
|
||||
|
||||
// query matches the full name of an object
|
||||
objNameMatch: 11,
|
||||
// or matches in the last dotted part of the object name
|
||||
objPartialMatch: 6,
|
||||
// Additive scores depending on the priority of the object
|
||||
objPrio: {0: 15, // used to be importantResults
|
||||
1: 5, // used to be objectResults
|
||||
2: -5}, // used to be unimportantResults
|
||||
// Used when the priority is not in the mapping.
|
||||
objPrioDefault: 0,
|
||||
|
||||
// query found in title
|
||||
title: 15,
|
||||
// query found in terms
|
||||
term: 5
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Search Module
|
||||
*/
|
||||
|
@ -138,13 +317,14 @@ var Search = {
|
|||
*/
|
||||
query : function(query) {
|
||||
var i;
|
||||
var stopwords = ["a","and","are","as","at","be","but","by","for","if","in","into","is","it","near","no","not","of","on","or","such","that","the","their","then","there","these","they","this","to","was","will","with"];
|
||||
|
||||
// stem the searchterms and add them to the correct list
|
||||
var stemmer = new Stemmer();
|
||||
var searchterms = [];
|
||||
var excluded = [];
|
||||
var hlterms = [];
|
||||
var tmp = splitQuery(query);
|
||||
var tmp = query.split(/\s+/);
|
||||
var objectterms = [];
|
||||
for (i = 0; i < tmp.length; i++) {
|
||||
if (tmp[i] !== "") {
|
||||
|
@ -158,10 +338,6 @@ var Search = {
|
|||
}
|
||||
// stem the word
|
||||
var word = stemmer.stemWord(tmp[i].toLowerCase());
|
||||
// prevent stemmer from cutting word smaller than two chars
|
||||
if(word.length < 3 && tmp[i].length >= 3) {
|
||||
word = tmp[i];
|
||||
}
|
||||
var toAppend;
|
||||
// select the correct list
|
||||
if (word[0] == '-') {
|
||||
|
@ -259,11 +435,7 @@ var Search = {
|
|||
displayNextItem();
|
||||
});
|
||||
} else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
|
||||
var suffix = DOCUMENTATION_OPTIONS.SOURCELINK_SUFFIX;
|
||||
if (suffix === undefined) {
|
||||
suffix = '.txt';
|
||||
}
|
||||
$.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[5] + (item[5].slice(-suffix.length) === suffix ? '' : suffix),
|
||||
$.ajax({url: DOCUMENTATION_OPTIONS.URL_ROOT + '_sources/' + item[0] + '.txt',
|
||||
dataType: "text",
|
||||
complete: function(jqxhr, textstatus) {
|
||||
var data = jqxhr.responseText;
|
||||
|
@ -302,7 +474,6 @@ var Search = {
|
|||
*/
|
||||
performObjectSearch : function(object, otherterms) {
|
||||
var filenames = this._index.filenames;
|
||||
var docnames = this._index.docnames;
|
||||
var objects = this._index.objects;
|
||||
var objnames = this._index.objnames;
|
||||
var titles = this._index.titles;
|
||||
|
@ -356,7 +527,7 @@ var Search = {
|
|||
} else {
|
||||
score += Scorer.objPrioDefault;
|
||||
}
|
||||
results.push([docnames[match[0]], fullname, '#'+anchor, descr, score, filenames[match[0]]]);
|
||||
results.push([filenames[match[0]], fullname, '#'+anchor, descr, score]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +539,6 @@ var Search = {
|
|||
* search for full-text terms in the index
|
||||
*/
|
||||
performTermsSearch : function(searchterms, excluded, terms, titleterms) {
|
||||
var docnames = this._index.docnames;
|
||||
var filenames = this._index.filenames;
|
||||
var titles = this._index.titles;
|
||||
|
||||
|
@ -443,7 +613,7 @@ var Search = {
|
|||
// select one (max) score for the file.
|
||||
// for better ranking, we should calculate ranking by using words statistics like basic tf-idf...
|
||||
var score = $u.max($u.map(fileMap[file], function(w){return scoreMap[file][w]}));
|
||||
results.push([docnames[file], titles[file], '', null, score, filenames[file]]);
|
||||
results.push([filenames[file], titles[file], '', null, score]);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
|
@ -478,4 +648,4 @@ var Search = {
|
|||
|
||||
$(document).ready(function() {
|
||||
Search.init();
|
||||
});
|
||||
});
|
Before Width: | Height: | Size: 214 B After Width: | Height: | Size: 345 B |
Before Width: | Height: | Size: 203 B After Width: | Height: | Size: 345 B |
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* sphinx.websupport utilities for all documentation.
|
||||
*
|
||||
* :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS.
|
||||
* :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
@ -301,7 +301,7 @@
|
|||
li.hide();
|
||||
|
||||
// Determine where in the parents children list to insert this comment.
|
||||
for(var i=0; i < siblings.length; i++) {
|
||||
for(i=0; i < siblings.length; i++) {
|
||||
if (comp(comment, siblings[i]) <= 0) {
|
||||
$('#cd' + siblings[i].id)
|
||||
.parent()
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<title>Contents — fmt 7.0.3 documentation</title>
|
||||
<title>Contents — fmt 7.1.3 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
@ -17,19 +17,16 @@
|
|||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '7.0.3',
|
||||
VERSION: '7.1.3',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
SOURCELINK_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<script type="text/javascript" src="_static/language_data.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Usage" href="usage.html" />
|
||||
<meta name="description" content="Small, safe and fast formatting library">
|
||||
<meta name="keywords" content="C++, formatting, printf, string, library">
|
||||
|
@ -68,15 +65,15 @@
|
|||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
|
||||
role="button" aria-expanded="false">7.0.3
|
||||
role="button" aria-expanded="false">7.1.3
|
||||
<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.3">7.0.3</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.2">7.0.2</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.2">7.1.2</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.1">7.0.1</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.1">7.1.1</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -134,9 +131,11 @@
|
|||
<li class="toctree-l2"><a class="reference internal" href="usage.html#building-the-library">Building the Library</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#installing-the-library">Installing the Library</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#usage-with-cmake">Usage with CMake</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#usage-with-build2">Usage with build2</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#building-the-documentation">Building the Documentation</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#conda">Conda</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#vcpkg">Vcpkg</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#lhelper">LHelper</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#android-ndk">Android NDK</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="usage.html#homebrew">Homebrew</a></li>
|
||||
</ul>
|
||||
|
@ -147,9 +146,10 @@
|
|||
<li class="toctree-l2"><a class="reference internal" href="api.html#ranges-and-tuple-formatting">Ranges and Tuple Formatting</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="api.html#date-and-time-formatting">Date and Time Formatting</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="api.html#format-string-compilation">Format string compilation</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="api.html#std-ostream-support"><code class="docutils literal notranslate"><span class="pre">std::ostream</span></code> Support</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="api.html#printf-formatting"><code class="docutils literal notranslate"><span class="pre">printf</span></code> Formatting</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="api.html#compatibility-with-c-20-std-format">Compatibility with C++20 <code class="docutils literal notranslate"><span class="pre">std::format</span></code></a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="api.html#terminal-color-and-text-style">Terminal color and text style</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="api.html#std-ostream-support"><code class="docutils literal"><span class="pre">std::ostream</span></code> Support</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="api.html#printf-formatting"><code class="docutils literal"><span class="pre">printf</span></code> Formatting</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="api.html#compatibility-with-c-20-std-format">Compatibility with C++20 <code class="docutils literal"><span class="pre">std::format</span></code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="syntax.html">Format String Syntax</a><ul>
|
||||
|
@ -170,7 +170,7 @@
|
|||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2012-present, Victor Zverovich.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.8.5.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.4.1+.
|
||||
</div>
|
||||
|
||||
<script src="_static/bootstrap.min.js"></script>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<title>Index — fmt 7.0.3 documentation</title>
|
||||
<title>Index — fmt 7.1.3 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
@ -18,19 +18,16 @@
|
|||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '7.0.3',
|
||||
VERSION: '7.1.3',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
SOURCELINK_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<script type="text/javascript" src="_static/language_data.js"></script>
|
||||
<link rel="index" title="Index" href="#" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<meta name="description" content="Small, safe and fast formatting library">
|
||||
<meta name="keywords" content="C++, formatting, printf, string, library">
|
||||
<meta name="author" content="Victor Zverovich">
|
||||
|
@ -68,15 +65,15 @@
|
|||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
|
||||
role="button" aria-expanded="false">7.0.3
|
||||
role="button" aria-expanded="false">7.1.3
|
||||
<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.3">7.0.3</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.2">7.0.2</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.2">7.1.2</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.1">7.0.1</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.1">7.1.1</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -134,134 +131,266 @@
|
|||
</div>
|
||||
<h2 id="F">F</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="api.html#_CPPv4N3fmt3argEPK4CharRK1T">fmt::arg (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt16basic_format_argE">fmt::basic_format_arg (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt17basic_format_argsE">fmt::basic_format_args (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt17basic_format_args17basic_format_argsEPK10format_argi">fmt::basic_format_args::basic_format_args (C++ function)</a>, <a href="api.html#_CPPv4N3fmt17basic_format_args17basic_format_argsERK16format_arg_storeI7ContextDp4ArgsE">[1]</a>, <a href="api.html#_CPPv4N3fmt17basic_format_args17basic_format_argsERK24dynamic_format_arg_storeI7ContextE">[2]</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4NK3fmt17basic_format_args3getEi">fmt::basic_format_args::get (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt26basic_format_parse_contextE">fmt::basic_format_parse_context (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt26basic_format_parse_context10advance_toE8iterator">fmt::basic_format_parse_context::advance_to (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4NK3fmt26basic_format_parse_context5beginEv">fmt::basic_format_parse_context::begin (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt26basic_format_parse_context12check_arg_idEi">fmt::basic_format_parse_context::check_arg_id (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4NK3fmt26basic_format_parse_context3endEv">fmt::basic_format_parse_context::end (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt26basic_format_parse_context11next_arg_idEv">fmt::basic_format_parse_context::next_arg_id (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt19basic_memory_bufferE">fmt::basic_memory_buffer (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt19basic_memory_buffer19basic_memory_bufferERR19basic_memory_buffer">fmt::basic_memory_buffer::basic_memory_buffer (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt19basic_memory_buffer4growE6size_t">fmt::basic_memory_buffer::grow (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt19basic_memory_bufferaSERR19basic_memory_buffer">fmt::basic_memory_buffer::operator= (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt17basic_string_viewE">fmt::basic_string_view (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt17basic_string_view17basic_string_viewEPK4Char">fmt::basic_string_view::basic_string_view (C++ function)</a>, <a href="api.html#_CPPv4N3fmt17basic_string_view17basic_string_viewEPK4Char6size_t">[1]</a>, <a href="api.html#_CPPv4N3fmt17basic_string_view17basic_string_viewERKNSt12basic_stringI4Char6Traits5AllocEE">[2]</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4NK3fmt17basic_string_view4dataEv">fmt::basic_string_view::data (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4NK3fmt17basic_string_view4sizeEv">fmt::basic_string_view::size (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt13buffered_file6vprintE11string_view11format_args">fmt::buffered_file::vprint (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt6detail6bufferE">fmt::detail::buffer (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt6detail6buffer6appendEPK1UPK1U">fmt::detail::buffer::append (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4NK3fmt6detail6buffer8capacityEv">fmt::detail::buffer::capacity (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt6detail6buffer5clearEv">fmt::detail::buffer::clear (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt6detail6buffer4dataEv">fmt::detail::buffer::data (C++ function)</a>, <a href="api.html#_CPPv4NK3fmt6detail6buffer4dataEv">[1]</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt6detail6buffer7reserveE6size_t">fmt::detail::buffer::reserve (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt6detail6buffer6resizeE6size_t">fmt::detail::buffer::resize (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4NK3fmt6detail6buffer4sizeEv">fmt::detail::buffer::size (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt24dynamic_format_arg_storeE">fmt::dynamic_format_arg_store (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt24dynamic_format_arg_store5clearEv">fmt::dynamic_format_arg_store::clear (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt24dynamic_format_arg_store9push_backENSt17reference_wrapperI1TEE">fmt::dynamic_format_arg_store::push_back (C++ function)</a>, <a href="api.html#_CPPv4N3fmt24dynamic_format_arg_store9push_backERK1T">[1]</a>, <a href="api.html#_CPPv4N3fmt24dynamic_format_arg_store9push_backERKN6detail9named_argI9char_type1TEE">[2]</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="api.html#_CPPv4N3fmt24dynamic_format_arg_store7reserveE6size_t6size_t">fmt::dynamic_format_arg_store::reserve (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt6formatERK1SDpRR4Args">fmt::format (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt16format_arg_storeE">fmt::format_arg_store (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt11format_argsE">fmt::format_args (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt19format_system_errorERN6detail6bufferIcEEi11string_view">fmt::format_system_error (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt9format_toE8OutputItRK1SDpRR4Args">fmt::format_to (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt11format_to_nE8OutputIt6size_tRK1SDpRK4Args">fmt::format_to_n (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt18format_to_n_resultE">fmt::format_to_n_result (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt18format_to_n_result3outE">fmt::format_to_n_result::out (C++ member)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt18format_to_n_result4sizeE">fmt::format_to_n_result::size (C++ member)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt14formatted_sizeE11string_viewDpRK4Args">fmt::formatted_size (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt7fprintfEPNSt4FILEERK1SDpRK4Args">fmt::fprintf (C++ function)</a>, <a href="api.html#_CPPv4N3fmt7fprintfERNSt13basic_ostreamI4CharEERK1SDpRK4Args">[1]</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt7is_charE">fmt::is_char (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt4joinE2It8Sentinel11string_view">fmt::join (C++ function)</a>, <a href="api.html#_CPPv4N3fmt4joinERK5Range11string_view">[1]</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt8literalsli2_aEPKc6size_t">fmt::literals::operator""_a (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt8literalsli7_formatEPKc6size_t">fmt::literals::operator""_format (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt16make_format_argsEDpRK4Args">fmt::make_format_args (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt5printEPNSt4FILEERK1SDpRR4Args">fmt::print (C++ function)</a>, <a href="api.html#_CPPv4N3fmt5printERK1SDpRR4Args">[1]</a>, <a href="api.html#_CPPv4N3fmt5printERNSt13basic_ostreamI4CharEERK1SDpRR4Args">[2]</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt6printfERK1SDpRK4Args">fmt::printf (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt7sprintfERK1SDpRK4Args">fmt::sprintf (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt12system_errorE">fmt::system_error (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt12system_error12system_errorEi11string_viewDpRK4Args">fmt::system_error::system_error (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt9to_stringERK1T">fmt::to_string (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt14to_string_viewEPK4Char">fmt::to_string_view (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt10to_wstringERK1T">fmt::to_wstring (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt7vformatERK1S17basic_format_argsI14buffer_contextI15type_identity_tI4CharEEE">fmt::vformat (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt6vprintEPNSt4FILEE11string_view11format_args">fmt::vprint (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt13windows_errorE">fmt::windows_error (C++ class)</a>
|
||||
</li>
|
||||
<li><a href="api.html#_CPPv4N3fmt13windows_error13windows_errorEi11string_viewDpRK4Args">fmt::windows_error::windows_error (C++ function)</a>
|
||||
</li>
|
||||
<li><a href="api.html#c.FMT_COMPILE">FMT_COMPILE (C macro)</a>
|
||||
</li>
|
||||
<li><a href="api.html#c.FMT_STRING">FMT_STRING (C macro)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%" valign="top"><dl>
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt3argEPK4CharRK1T">fmt::arg (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt16basic_format_argE">fmt::basic_format_arg (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt17basic_format_argsE">fmt::basic_format_args (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt17basic_format_args17basic_format_argsEPK10format_argi">fmt::basic_format_args::basic_format_args (C++ function)</a>, <a href="api.html#_CPPv2N3fmt17basic_format_args17basic_format_argsERK16format_arg_storeI7ContextDp4ArgsE">[1]</a>, <a href="api.html#_CPPv2N3fmt17basic_format_args17basic_format_argsERK24dynamic_format_arg_storeI7ContextE">[2]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2NK3fmt17basic_format_args3getEi">fmt::basic_format_args::get (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt26basic_format_parse_contextE">fmt::basic_format_parse_context (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt26basic_format_parse_context10advance_toE8iterator">fmt::basic_format_parse_context::advance_to (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2NK3fmt26basic_format_parse_context5beginEv">fmt::basic_format_parse_context::begin (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt26basic_format_parse_context12check_arg_idEi">fmt::basic_format_parse_context::check_arg_id (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2NK3fmt26basic_format_parse_context3endEv">fmt::basic_format_parse_context::end (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt26basic_format_parse_context11next_arg_idEv">fmt::basic_format_parse_context::next_arg_id (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt19basic_memory_bufferE">fmt::basic_memory_buffer (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt19basic_memory_buffer19basic_memory_bufferERR19basic_memory_buffer">fmt::basic_memory_buffer::basic_memory_buffer (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt19basic_memory_buffer4growE6size_t">fmt::basic_memory_buffer::grow (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt19basic_memory_bufferaSERR19basic_memory_buffer">fmt::basic_memory_buffer::operator= (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt19basic_memory_buffer7reserveE6size_t">fmt::basic_memory_buffer::reserve (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt19basic_memory_buffer6resizeE6size_t">fmt::basic_memory_buffer::resize (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt17basic_string_viewE">fmt::basic_string_view (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt17basic_string_view17basic_string_viewEPK4Char">fmt::basic_string_view::basic_string_view (C++ function)</a>, <a href="api.html#_CPPv2N3fmt17basic_string_view17basic_string_viewEPK4Char6size_t">[1]</a>, <a href="api.html#_CPPv2N3fmt17basic_string_view17basic_string_viewERKNSt12basic_stringI4Char6Traits5AllocEE">[2]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2NK3fmt17basic_string_view4dataEv">fmt::basic_string_view::data (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2NK3fmt17basic_string_view4sizeEv">fmt::basic_string_view::size (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt13buffered_file6vprintE11string_view11format_args">fmt::buffered_file::vprint (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt6detail6bufferE">fmt::detail::buffer (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt6detail6buffer6appendEPK1UPK1U">fmt::detail::buffer::append (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2NK3fmt6detail6buffer8capacityEv">fmt::detail::buffer::capacity (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt6detail6buffer5clearEv">fmt::detail::buffer::clear (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt6detail6buffer4dataEv">fmt::detail::buffer::data (C++ function)</a>, <a href="api.html#_CPPv2NK3fmt6detail6buffer4dataEv">[1]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2NK3fmt6detail6buffer4sizeEv">fmt::detail::buffer::size (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt24dynamic_format_arg_storeE">fmt::dynamic_format_arg_store (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt24dynamic_format_arg_store5clearEv">fmt::dynamic_format_arg_store::clear (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt24dynamic_format_arg_store9push_backENSt17reference_wrapperI1TEE">fmt::dynamic_format_arg_store::push_back (C++ function)</a>, <a href="api.html#_CPPv2N3fmt24dynamic_format_arg_store9push_backERK1T">[1]</a>, <a href="api.html#_CPPv2N3fmt24dynamic_format_arg_store9push_backERKN6detail9named_argI9char_type1TEE">[2]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt24dynamic_format_arg_store7reserveE6size_t6size_t">fmt::dynamic_format_arg_store::reserve (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
</dl></td>
|
||||
<td style="width: 33%" valign="top"><dl>
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt6formatERK1SDpRR4Args">fmt::format (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt16format_arg_storeE">fmt::format_arg_store (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt11format_argsE">fmt::format_args (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt19format_system_errorERN6detail6bufferIcEEi11string_view">fmt::format_system_error (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt9format_toE8OutputItRK1SDpRR4Args">fmt::format_to (C++ function)</a>, <a href="api.html#_CPPv2N3fmt9format_toE8OutputItRK1SDpRR4Args">[1]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt11format_to_nE8OutputIt6size_tRK1SDpRK4Args">fmt::format_to_n (C++ function)</a>, <a href="api.html#_CPPv2N3fmt11format_to_nE8OutputIt6size_tRK1SDpRK4Args">[1]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt18format_to_n_resultE">fmt::format_to_n_result (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt18format_to_n_result3outE">fmt::format_to_n_result::out (C++ member)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt18format_to_n_result4sizeE">fmt::format_to_n_result::size (C++ member)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRR4Args">fmt::formatted_size (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt7fprintfEPNSt4FILEERK1SDpRK4Args">fmt::fprintf (C++ function)</a>, <a href="api.html#_CPPv2N3fmt7fprintfERNSt13basic_ostreamI4CharEERK1SDpRK4Args">[1]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt7is_charE">fmt::is_char (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt4joinE2It8Sentinel11string_view">fmt::join (C++ function)</a>, <a href="api.html#_CPPv2N3fmt4joinERR5Range11string_view">[1]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt8literalsli2_aEPKc6size_t">fmt::literals::operator""_a (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt8literalsli7_formatEPKc6size_t">fmt::literals::operator""_format (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt17make_args_checkedERK1SDpRK18remove_reference_tI4ArgsE">fmt::make_args_checked (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt16make_format_argsEDpRK4Args">fmt::make_format_args (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt5printEPNSt4FILEERK1SDpRR4Args">fmt::print (C++ function)</a>, <a href="api.html#_CPPv2N3fmt5printERK10text_styleRK1SDpRK4Args">[1]</a>, <a href="api.html#_CPPv2N3fmt5printERK1SDpRR4Args">[2]</a>, <a href="api.html#_CPPv2N3fmt5printERNSt13basic_ostreamI4CharEERK1SDpRR4Args">[3]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt6printfERK1SDpRK4Args">fmt::printf (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt3ptrEPK1T">fmt::ptr (C++ function)</a>, <a href="api.html#_CPPv2N3fmt3ptrERKNSt10shared_ptrI1TEE">[1]</a>, <a href="api.html#_CPPv2N3fmt3ptrERKNSt10unique_ptrI1TEE">[2]</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt7sprintfERK1SDpRK4Args">fmt::sprintf (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt12system_errorE">fmt::system_error (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt12system_error12system_errorEi11string_viewDpRK4Args">fmt::system_error::system_error (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt9to_stringERK1T">fmt::to_string (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt14to_string_viewEPK4Char">fmt::to_string_view (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt10to_wstringERK1T">fmt::to_wstring (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt7vformatERK1S17basic_format_argsI14buffer_contextI15type_identity_tI4CharEEE">fmt::vformat (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt6vprintEPNSt4FILEE11string_view11format_args">fmt::vprint (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt13windows_errorE">fmt::windows_error (C++ class)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#_CPPv2N3fmt13windows_error13windows_errorEi11string_viewDpRK4Args">fmt::windows_error::windows_error (C++ function)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#c.FMT_COMPILE">FMT_COMPILE (C macro)</a>
|
||||
</dt>
|
||||
|
||||
|
||||
<dt><a href="api.html#c.FMT_STRING">FMT_STRING (C macro)</a>
|
||||
</dt>
|
||||
|
||||
</dl></td>
|
||||
</tr></table>
|
||||
|
||||
|
||||
|
@ -274,7 +403,7 @@
|
|||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2012-present, Victor Zverovich.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.8.5.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.4.1+.
|
||||
</div>
|
||||
|
||||
<script src="_static/bootstrap.min.js"></script>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<title>Overview — fmt 7.0.3 documentation</title>
|
||||
<title>Overview — fmt 7.1.3 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
@ -17,19 +17,16 @@
|
|||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '7.0.3',
|
||||
VERSION: '7.1.3',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
SOURCELINK_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<script type="text/javascript" src="_static/language_data.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<meta name="description" content="Small, safe and fast formatting library">
|
||||
<meta name="keywords" content="C++, formatting, printf, string, library">
|
||||
<meta name="author" content="Victor Zverovich">
|
||||
|
@ -67,15 +64,15 @@
|
|||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
|
||||
role="button" aria-expanded="false">7.0.3
|
||||
role="button" aria-expanded="false">7.1.3
|
||||
<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.3">7.0.3</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.2">7.0.2</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.2">7.1.2</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.1">7.0.1</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.1">7.1.1</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -124,7 +121,7 @@
|
|||
<div class="btn-group" role="group">
|
||||
|
||||
<a class="btn btn-success"
|
||||
href="https://github.com/fmtlib/fmt/releases/download/7.0.3/fmt-7.0.3.zip">
|
||||
href="https://github.com/fmtlib/fmt/releases/download/7.1.3/fmt-7.1.3.zip">
|
||||
<span class="glyphicon glyphicon-download"></span> Download
|
||||
</a>
|
||||
<button type="button" class="btn btn-success dropdown-toggle"
|
||||
|
@ -132,15 +129,15 @@
|
|||
<ul class="dropdown-menu">
|
||||
|
||||
|
||||
<li><a href="https://github.com/fmtlib/fmt/releases/download/7.0.3/fmt-7.0.3.zip">Version 7.0.3
|
||||
<li><a href="https://github.com/fmtlib/fmt/releases/download/7.1.3/fmt-7.1.3.zip">Version 7.1.3
|
||||
</a></li>
|
||||
|
||||
|
||||
<li><a href="https://github.com/fmtlib/fmt/releases/download/7.0.2/fmt-7.0.2.zip">Version 7.0.2
|
||||
<li><a href="https://github.com/fmtlib/fmt/releases/download/7.1.2/fmt-7.1.2.zip">Version 7.1.2
|
||||
</a></li>
|
||||
|
||||
|
||||
<li><a href="https://github.com/fmtlib/fmt/releases/download/7.0.1/fmt-7.0.1.zip">Version 7.0.1
|
||||
<li><a href="https://github.com/fmtlib/fmt/releases/download/7.1.1/fmt-7.1.1.zip">Version 7.1.1
|
||||
</a></li>
|
||||
|
||||
</ul>
|
||||
|
@ -170,44 +167,42 @@ alternative to C stdio and C++ iostreams.</p>
|
|||
</div>
|
||||
</div><div class="section" id="format-api">
|
||||
<span id="format-api-intro"></span><h2>Format API<a class="headerlink" href="#format-api" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The format API is similar in spirit to the C <code class="docutils literal notranslate"><span class="pre">printf</span></code> family of function but
|
||||
is safer, simpler and several times <a class="reference external" href="http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html">faster</a>
|
||||
<p>The format API is similar in spirit to the C <code class="docutils literal"><span class="pre">printf</span></code> family of function but
|
||||
is safer, simpler and several times <a class="reference external" href="https://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html">faster</a>
|
||||
than common standard library implementations.
|
||||
The <a class="reference external" href="syntax.html">format string syntax</a> is similar to the one used by
|
||||
<a class="reference external" href="http://docs.python.org/3/library/stdtypes.html#str.format">str.format</a> in
|
||||
Python:</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"The answer is {}."</span><span class="p">,</span> <span class="mi">42</span><span class="p">);</span>
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"The answer is {}."</span><span class="p">,</span> <span class="mi">42</span><span class="p">);</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">fmt::format</span></code> function returns a string “The answer is 42.”. You can use
|
||||
<code class="docutils literal notranslate"><span class="pre">fmt::memory_buffer</span></code> to avoid constructing <code class="docutils literal notranslate"><span class="pre">std::string</span></code>:</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">memory_buffer</span> <span class="n">out</span><span class="p">;</span>
|
||||
<p>The <code class="docutils literal"><span class="pre">fmt::format</span></code> function returns a string “The answer is 42.”. You can use
|
||||
<code class="docutils literal"><span class="pre">fmt::memory_buffer</span></code> to avoid constructing <code class="docutils literal"><span class="pre">std::string</span></code>:</p>
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">memory_buffer</span> <span class="n">out</span><span class="p">;</span>
|
||||
<span class="n">format_to</span><span class="p">(</span><span class="n">out</span><span class="p">,</span> <span class="s">"For a moment, {} happened."</span><span class="p">,</span> <span class="s">"nothing"</span><span class="p">);</span>
|
||||
<span class="n">out</span><span class="p">.</span><span class="n">data</span><span class="p">();</span> <span class="c1">// returns a pointer to the formatted data</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">fmt::print</span></code> function performs formatting and writes the result to a stream:</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"System error code = {}</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">errno</span><span class="p">);</span>
|
||||
<p>The <code class="docutils literal"><span class="pre">fmt::print</span></code> function performs formatting and writes the result to a stream:</p>
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"System error code = {}</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">errno</span><span class="p">);</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The file argument can be omitted in which case the function prints to
|
||||
<code class="docutils literal notranslate"><span class="pre">stdout</span></code>:</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">"Don't {}</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="s">"panic"</span><span class="p">);</span>
|
||||
<p>If you omit the file argument the function will print to <code class="docutils literal"><span class="pre">stdout</span></code>:</p>
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">"Don't {}</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="s">"panic"</span><span class="p">);</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The Format API also supports positional arguments useful for localization:</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">"I'd rather be {1} than {0}."</span><span class="p">,</span> <span class="s">"right"</span><span class="p">,</span> <span class="s">"happy"</span><span class="p">);</span>
|
||||
<p>The format API also supports positional arguments useful for localization:</p>
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">"I'd rather be {1} than {0}."</span><span class="p">,</span> <span class="s">"right"</span><span class="p">,</span> <span class="s">"happy"</span><span class="p">);</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Named arguments can be created with <code class="docutils literal notranslate"><span class="pre">fmt::arg</span></code>. This makes it easier to track
|
||||
what goes where when multiple arguments are being formatted:</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">"Hello, {name}! The answer is {number}. Goodbye, {name}."</span><span class="p">,</span>
|
||||
<p>You can pass named arguments with <code class="docutils literal"><span class="pre">fmt::arg</span></code>:</p>
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">"Hello, {name}! The answer is {number}. Goodbye, {name}."</span><span class="p">,</span>
|
||||
<span class="n">fmt</span><span class="o">::</span><span class="n">arg</span><span class="p">(</span><span class="s">"name"</span><span class="p">,</span> <span class="s">"World"</span><span class="p">),</span> <span class="n">fmt</span><span class="o">::</span><span class="n">arg</span><span class="p">(</span><span class="s">"number"</span><span class="p">,</span> <span class="mi">42</span><span class="p">));</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>If your compiler supports C++11 user-defined literals, the suffix <code class="docutils literal notranslate"><span class="pre">_a</span></code> offers
|
||||
<p>If your compiler supports C++11 user-defined literals, the suffix <code class="docutils literal"><span class="pre">_a</span></code> offers
|
||||
an alternative, slightly terser syntax for named arguments:</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="k">using</span> <span class="k">namespace</span> <span class="n">fmt</span><span class="o">::</span><span class="n">literals</span><span class="p">;</span>
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span><span class="k">using</span> <span class="k">namespace</span> <span class="n">fmt</span><span class="o">::</span><span class="n">literals</span><span class="p">;</span>
|
||||
<span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">"Hello, {name}! The answer is {number}. Goodbye, {name}."</span><span class="p">,</span>
|
||||
<span class="s">"name"</span><span class="n">_a</span><span class="o">=</span><span class="s">"World"</span><span class="p">,</span> <span class="s">"number"</span><span class="n">_a</span><span class="o">=</span><span class="mi">42</span><span class="p">);</span>
|
||||
</pre></div>
|
||||
|
@ -218,36 +213,36 @@ an alternative, slightly terser syntax for named arguments:</p>
|
|||
<p>The library is fully type safe, automatic memory management prevents buffer
|
||||
overflow, errors in format strings are reported using exceptions or at compile
|
||||
time. For example, the code</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"The answer is {:d}"</span><span class="p">,</span> <span class="s">"forty-two"</span><span class="p">);</span>
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"The answer is {:d}"</span><span class="p">,</span> <span class="s">"forty-two"</span><span class="p">);</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>throws a <code class="docutils literal notranslate"><span class="pre">format_error</span></code> exception with description “unknown format code ‘d’ for
|
||||
string”, because the argument <code class="docutils literal notranslate"><span class="pre">"forty-two"</span></code> is a string while the format code
|
||||
<code class="docutils literal notranslate"><span class="pre">d</span></code> only applies to integers, while</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">format</span><span class="p">(</span><span class="n">FMT_STRING</span><span class="p">(</span><span class="s">"The answer is {:d}"</span><span class="p">),</span> <span class="s">"forty-two"</span><span class="p">);</span>
|
||||
<p>throws the <code class="docutils literal"><span class="pre">format_error</span></code> exception because the argument <code class="docutils literal"><span class="pre">"forty-two"</span></code> is a
|
||||
string while the format code <code class="docutils literal"><span class="pre">d</span></code> only applies to integers.</p>
|
||||
<p>The code</p>
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span><span class="n">format</span><span class="p">(</span><span class="n">FMT_STRING</span><span class="p">(</span><span class="s">"The answer is {:d}"</span><span class="p">),</span> <span class="s">"forty-two"</span><span class="p">);</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>reports a compile-time error for the same reason on compilers that support
|
||||
relaxed <code class="docutils literal notranslate"><span class="pre">constexpr</span></code>. See <a class="reference external" href="api.html#c.fmt">here</a> for details.</p>
|
||||
<p>reports a compile-time error on compilers that support relaxed <code class="docutils literal"><span class="pre">constexpr</span></code>.
|
||||
See <a class="reference external" href="api.html#c.fmt">here</a> for details.</p>
|
||||
<p>The following code</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span>fmt::format("Cyrillic letter {}", L'\x42e');
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span>fmt::format("Cyrillic letter {}", L'\x42e');
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>produces a compile-time error because wide character <code class="docutils literal notranslate"><span class="pre">L'\x42e'</span></code> cannot be
|
||||
<p>produces a compile-time error because wide character <code class="docutils literal"><span class="pre">L'\x42e'</span></code> cannot be
|
||||
formatted into a narrow string. You can use a wide format string instead:</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span>fmt::format(L"Cyrillic letter {}", L'\x42e');
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span>fmt::format(L"Cyrillic letter {}", L'\x42e');
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>For comparison, writing a wide character to <code class="docutils literal notranslate"><span class="pre">std::ostream</span></code> results in
|
||||
its numeric value being written to the stream (i.e. 1070 instead of letter ‘ю’
|
||||
which is represented by <code class="docutils literal notranslate"><span class="pre">L'\x42e'</span></code> if we use Unicode) which is rarely what is
|
||||
needed.</p>
|
||||
<p>For comparison, writing a wide character to <code class="docutils literal"><span class="pre">std::ostream</span></code> results in
|
||||
its numeric value being written to the stream (i.e. 1070 instead of letter ‘ю’
|
||||
which is represented by <code class="docutils literal"><span class="pre">L'\x42e'</span></code> if we use Unicode) which is rarely
|
||||
desirable.</p>
|
||||
</div>
|
||||
<div class="section" id="compact-binary-code">
|
||||
<h2>Compact Binary Code<a class="headerlink" href="#compact-binary-code" title="Permalink to this headline">¶</a></h2>
|
||||
<p>The library is designed to produce compact per-call compiled code. For example
|
||||
<p>The library produces compact per-call compiled code. For example
|
||||
(<a class="reference external" href="https://godbolt.org/g/TZU4KF">godbolt</a>),</p>
|
||||
<div class="code c++ highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><fmt/core.h></span><span class="cp"></span>
|
||||
<div class="code c++ highlight-c++"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><fmt/core.h></span><span class="cp"></span>
|
||||
|
||||
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
|
||||
<span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">"The answer is {}."</span><span class="p">,</span> <span class="mi">42</span><span class="p">);</span>
|
||||
|
@ -255,14 +250,14 @@ needed.</p>
|
|||
</pre></div>
|
||||
</div>
|
||||
<p>compiles to just</p>
|
||||
<div class="code asm highlight-c++ notranslate"><div class="highlight"><pre><span></span>main: # @main
|
||||
<div class="code asm highlight-c++"><div class="highlight"><pre><span></span>main: # @main
|
||||
sub rsp, 24
|
||||
mov qword ptr [rsp], 42
|
||||
mov rcx, rsp
|
||||
mov edi, offset .L.str
|
||||
mov esi, 17
|
||||
mov edx, 2
|
||||
call fmt::v5::vprint(fmt::v5::basic_string_view<char>, fmt::v5::format_args)
|
||||
mov edx, 1
|
||||
call fmt::v7::vprint(fmt::v7::basic_string_view<char>, fmt::v7::format_args)
|
||||
xor eax, eax
|
||||
add rsp, 24
|
||||
ret
|
||||
|
@ -283,16 +278,15 @@ needed.</p>
|
|||
<li>deleted functions</li>
|
||||
<li>alias templates</li>
|
||||
</ul>
|
||||
<p>These are available since GCC 4.8, Clang 3.0 and MSVC 19.0 (2015). For older
|
||||
compilers use {fmt} <a class="reference external" href="https://github.com/fmtlib/fmt/releases/tag/4.1.0">version 4.x</a> which continues to be
|
||||
maintained and only requires C++98.</p>
|
||||
<p>The output of all formatting functions is consistent across platforms. In
|
||||
particular, formatting a floating-point infinity always gives <code class="docutils literal notranslate"><span class="pre">inf</span></code> while the
|
||||
output of <code class="docutils literal notranslate"><span class="pre">printf</span></code> is platform-dependent. For example,</p>
|
||||
<div class="code highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">numeric_limits</span><span class="o"><</span><span class="kt">double</span><span class="o">>::</span><span class="n">infinity</span><span class="p">());</span>
|
||||
<p>These are available in GCC 4.8, Clang 3.0, MSVC 19.0 (2015) and more recent
|
||||
compiler version. For older compilers use {fmt} <a class="reference external" href="https://github.com/fmtlib/fmt/releases/tag/4.1.0">version 4.x</a> which is maintained and
|
||||
only requires C++98.</p>
|
||||
<p>The output of all formatting functions is consistent across platforms.
|
||||
For example,</p>
|
||||
<div class="code highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">std</span><span class="o">::</span><span class="n">numeric_limits</span><span class="o"><</span><span class="kt">double</span><span class="o">>::</span><span class="n">infinity</span><span class="p">());</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>always prints <code class="docutils literal notranslate"><span class="pre">inf</span></code>.</p>
|
||||
<p>always prints <code class="docutils literal"><span class="pre">inf</span></code> while the output of <code class="docutils literal"><span class="pre">printf</span></code> is platform-dependent.</p>
|
||||
</div>
|
||||
<div class="section" id="ease-of-use">
|
||||
<span id="id3"></span><h2>Ease of Use<a class="headerlink" href="#ease-of-use" title="Permalink to this headline">¶</a></h2>
|
||||
|
@ -317,7 +311,7 @@ using the library both in open-source and commercial projects.</p>
|
|||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2012-present, Victor Zverovich.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.8.5.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.4.1+.
|
||||
</div>
|
||||
|
||||
<script src="_static/bootstrap.min.js"></script>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<title>Search — fmt 7.0.3 documentation</title>
|
||||
<title>Search — fmt 7.1.3 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
@ -17,20 +17,17 @@
|
|||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '7.0.3',
|
||||
VERSION: '7.1.3',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
SOURCELINK_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<script type="text/javascript" src="_static/language_data.js"></script>
|
||||
<script type="text/javascript" src="_static/searchtools.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="#" />
|
||||
<script type="text/javascript">
|
||||
jQuery(function() { Search.loadIndex("searchindex.js"); });
|
||||
</script>
|
||||
|
@ -75,15 +72,15 @@
|
|||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
|
||||
role="button" aria-expanded="false">7.0.3
|
||||
role="button" aria-expanded="false">7.1.3
|
||||
<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.3">7.0.3</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.2">7.0.2</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.2">7.1.2</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.1">7.0.1</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.1">7.1.1</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -160,7 +157,7 @@
|
|||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2012-present, Victor Zverovich.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.8.5.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.4.1+.
|
||||
</div>
|
||||
|
||||
<script src="_static/bootstrap.min.js"></script>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<title>Format String Syntax — fmt 7.0.3 documentation</title>
|
||||
<title>Format String Syntax — fmt 7.1.3 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
@ -17,19 +17,16 @@
|
|||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '7.0.3',
|
||||
VERSION: '7.1.3',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
SOURCELINK_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<script type="text/javascript" src="_static/language_data.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="prev" title="API Reference" href="api.html" />
|
||||
<meta name="description" content="Small, safe and fast formatting library">
|
||||
<meta name="keywords" content="C++, formatting, printf, string, library">
|
||||
|
@ -68,15 +65,15 @@
|
|||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
|
||||
role="button" aria-expanded="false">7.0.3
|
||||
role="button" aria-expanded="false">7.1.3
|
||||
<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.3">7.0.3</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.2">7.0.2</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.2">7.1.2</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.1">7.0.1</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.1">7.1.1</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -131,40 +128,40 @@
|
|||
<p>Formatting functions such as <a class="reference internal" href="api.html#format"><span class="std std-ref">fmt::format()</span></a> and
|
||||
<a class="reference internal" href="api.html#print"><span class="std std-ref">fmt::print()</span></a> use the same format string syntax described in this
|
||||
section.</p>
|
||||
<p>Format strings contain “replacement fields” surrounded by curly braces <code class="docutils literal notranslate"><span class="pre">{}</span></code>.
|
||||
<p>Format strings contain “replacement fields” surrounded by curly braces <code class="docutils literal"><span class="pre">{}</span></code>.
|
||||
Anything that is not contained in braces is considered literal text, which is
|
||||
copied unchanged to the output. If you need to include a brace character in the
|
||||
literal text, it can be escaped by doubling: <code class="docutils literal notranslate"><span class="pre">{{</span></code> and <code class="docutils literal notranslate"><span class="pre">}}</span></code>.</p>
|
||||
literal text, it can be escaped by doubling: <code class="docutils literal"><span class="pre">{{</span></code> and <code class="docutils literal"><span class="pre">}}</span></code>.</p>
|
||||
<p>The grammar for a replacement field is as follows:</p>
|
||||
<pre>
|
||||
<strong id="grammar-token-replacement-field">replacement_field</strong> ::= "{" [<a class="reference internal" href="#grammar-token-arg-id"><code class="xref docutils literal notranslate"><span class="pre">arg_id</span></code></a>] [":" <a class="reference internal" href="#grammar-token-format-spec"><code class="xref docutils literal notranslate"><span class="pre">format_spec</span></code></a>] "}"
|
||||
<strong id="grammar-token-arg-id">arg_id </strong> ::= <a class="reference internal" href="#grammar-token-integer"><code class="xref docutils literal notranslate"><span class="pre">integer</span></code></a> | <a class="reference internal" href="#grammar-token-identifier"><code class="xref docutils literal notranslate"><span class="pre">identifier</span></code></a>
|
||||
<strong id="grammar-token-integer">integer </strong> ::= <a class="reference internal" href="#grammar-token-digit"><code class="xref docutils literal notranslate"><span class="pre">digit</span></code></a>+
|
||||
<strong id="grammar-token-replacement_field">replacement_field</strong> ::= "{" [<a class="reference internal" href="#grammar-token-arg_id"><code class="xref docutils literal"><span class="pre">arg_id</span></code></a>] [":" <a class="reference internal" href="#grammar-token-format_spec"><code class="xref docutils literal"><span class="pre">format_spec</span></code></a>] "}"
|
||||
<strong id="grammar-token-arg_id">arg_id </strong> ::= <a class="reference internal" href="#grammar-token-integer"><code class="xref docutils literal"><span class="pre">integer</span></code></a> | <a class="reference internal" href="#grammar-token-identifier"><code class="xref docutils literal"><span class="pre">identifier</span></code></a>
|
||||
<strong id="grammar-token-integer">integer </strong> ::= <a class="reference internal" href="#grammar-token-digit"><code class="xref docutils literal"><span class="pre">digit</span></code></a>+
|
||||
<strong id="grammar-token-digit">digit </strong> ::= "0"..."9"
|
||||
<strong id="grammar-token-identifier">identifier </strong> ::= <a class="reference internal" href="#grammar-token-id-start"><code class="xref docutils literal notranslate"><span class="pre">id_start</span></code></a> <a class="reference internal" href="#grammar-token-id-continue"><code class="xref docutils literal notranslate"><span class="pre">id_continue</span></code></a>*
|
||||
<strong id="grammar-token-id-start">id_start </strong> ::= "a"..."z" | "A"..."Z" | "_"
|
||||
<strong id="grammar-token-id-continue">id_continue </strong> ::= <a class="reference internal" href="#grammar-token-id-start"><code class="xref docutils literal notranslate"><span class="pre">id_start</span></code></a> | <a class="reference internal" href="#grammar-token-digit"><code class="xref docutils literal notranslate"><span class="pre">digit</span></code></a>
|
||||
<strong id="grammar-token-identifier">identifier </strong> ::= <a class="reference internal" href="#grammar-token-id_start"><code class="xref docutils literal"><span class="pre">id_start</span></code></a> <a class="reference internal" href="#grammar-token-id_continue"><code class="xref docutils literal"><span class="pre">id_continue</span></code></a>*
|
||||
<strong id="grammar-token-id_start">id_start </strong> ::= "a"..."z" | "A"..."Z" | "_"
|
||||
<strong id="grammar-token-id_continue">id_continue </strong> ::= <a class="reference internal" href="#grammar-token-id_start"><code class="xref docutils literal"><span class="pre">id_start</span></code></a> | <a class="reference internal" href="#grammar-token-digit"><code class="xref docutils literal"><span class="pre">digit</span></code></a>
|
||||
</pre>
|
||||
<p>In less formal terms, the replacement field can start with an <em>arg_id</em>
|
||||
that specifies the argument whose value is to be formatted and inserted into
|
||||
the output instead of the replacement field.
|
||||
The <em>arg_id</em> is optionally followed by a <em>format_spec</em>, which is preceded
|
||||
by a colon <code class="docutils literal notranslate"><span class="pre">':'</span></code>. These specify a non-default format for the replacement value.</p>
|
||||
by a colon <code class="docutils literal"><span class="pre">':'</span></code>. These specify a non-default format for the replacement value.</p>
|
||||
<p>See also the <a class="reference internal" href="#formatspec"><span class="std std-ref">Format Specification Mini-Language</span></a> section.</p>
|
||||
<p>If the numerical arg_ids in a format string are 0, 1, 2, … in sequence,
|
||||
they can all be omitted (not just some) and the numbers 0, 1, 2, … will be
|
||||
<p>If the numerical arg_ids in a format string are 0, 1, 2, ... in sequence,
|
||||
they can all be omitted (not just some) and the numbers 0, 1, 2, ... will be
|
||||
automatically inserted in that order.</p>
|
||||
<p>Named arguments can be referred to by their names or indices.</p>
|
||||
<p>Some simple format string examples:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="s">"First, thou shalt count to {0}"</span> <span class="c1">// References the first argument</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="s">"First, thou shalt count to {0}"</span> <span class="c1">// References the first argument</span>
|
||||
<span class="s">"Bring me a {}"</span> <span class="c1">// Implicitly references the first argument</span>
|
||||
<span class="s">"From {} to {}"</span> <span class="c1">// Same as "From {0} to {1}"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The <em>format_spec</em> field contains a specification of how the value should be
|
||||
presented, including such details as field width, alignment, padding, decimal
|
||||
precision and so on. Each value type can define its own “formatting
|
||||
mini-language” or interpretation of the <em>format_spec</em>.</p>
|
||||
precision and so on. Each value type can define its own “formatting
|
||||
mini-language” or interpretation of the <em>format_spec</em>.</p>
|
||||
<p>Most built-in types support a common formatting mini-language, which is
|
||||
described in the next section.</p>
|
||||
<p>A <em>format_spec</em> field can also include nested replacement fields in certain
|
||||
|
@ -174,7 +171,7 @@ of a value to be dynamically specified.</p>
|
|||
<p>See the <a class="reference internal" href="#formatexamples"><span class="std std-ref">Format Examples</span></a> section for some examples.</p>
|
||||
<div class="section" id="format-specification-mini-language">
|
||||
<span id="formatspec"></span><h2>Format Specification Mini-Language<a class="headerlink" href="#format-specification-mini-language" title="Permalink to this headline">¶</a></h2>
|
||||
<p>“Format specifications” are used within replacement fields contained within a
|
||||
<p>“Format specifications” are used within replacement fields contained within a
|
||||
format string to define how individual values are presented (see
|
||||
<a class="reference internal" href="#syntax"><span class="std std-ref">Format String Syntax</span></a>). Each formattable type may define how the format
|
||||
specification is to be interpreted.</p>
|
||||
|
@ -182,17 +179,17 @@ specification is to be interpreted.</p>
|
|||
although some of the formatting options are only supported by the numeric types.</p>
|
||||
<p>The general form of a <em>standard format specifier</em> is:</p>
|
||||
<pre>
|
||||
<strong id="grammar-token-format-spec">format_spec</strong> ::= [[<a class="reference internal" href="#grammar-token-fill"><code class="xref docutils literal notranslate"><span class="pre">fill</span></code></a>]<a class="reference internal" href="#grammar-token-align"><code class="xref docutils literal notranslate"><span class="pre">align</span></code></a>][<a class="reference internal" href="#grammar-token-sign"><code class="xref docutils literal notranslate"><span class="pre">sign</span></code></a>]["#"]["0"][<a class="reference internal" href="#grammar-token-width"><code class="xref docutils literal notranslate"><span class="pre">width</span></code></a>]["." <a class="reference internal" href="#grammar-token-precision"><code class="xref docutils literal notranslate"><span class="pre">precision</span></code></a>][<a class="reference internal" href="#grammar-token-type"><code class="xref docutils literal notranslate"><span class="pre">type</span></code></a>]
|
||||
<strong id="grammar-token-format_spec">format_spec</strong> ::= [[<a class="reference internal" href="#grammar-token-fill"><code class="xref docutils literal"><span class="pre">fill</span></code></a>]<a class="reference internal" href="#grammar-token-align"><code class="xref docutils literal"><span class="pre">align</span></code></a>][<a class="reference internal" href="#grammar-token-sign"><code class="xref docutils literal"><span class="pre">sign</span></code></a>]["#"]["0"][<a class="reference internal" href="#grammar-token-width"><code class="xref docutils literal"><span class="pre">width</span></code></a>]["." <a class="reference internal" href="#grammar-token-precision"><code class="xref docutils literal"><span class="pre">precision</span></code></a>][<a class="reference internal" href="#grammar-token-type"><code class="xref docutils literal"><span class="pre">type</span></code></a>]
|
||||
<strong id="grammar-token-fill">fill </strong> ::= <a character other than '{' or '}'>
|
||||
<strong id="grammar-token-align">align </strong> ::= "<" | ">" | "^"
|
||||
<strong id="grammar-token-sign">sign </strong> ::= "+" | "-" | " "
|
||||
<strong id="grammar-token-width">width </strong> ::= <a class="reference internal" href="#grammar-token-integer"><code class="xref docutils literal notranslate"><span class="pre">integer</span></code></a> | "{" [<a class="reference internal" href="#grammar-token-arg-id"><code class="xref docutils literal notranslate"><span class="pre">arg_id</span></code></a>] "}"
|
||||
<strong id="grammar-token-precision">precision </strong> ::= <a class="reference internal" href="#grammar-token-integer"><code class="xref docutils literal notranslate"><span class="pre">integer</span></code></a> | "{" [<a class="reference internal" href="#grammar-token-arg-id"><code class="xref docutils literal notranslate"><span class="pre">arg_id</span></code></a>] "}"
|
||||
<strong id="grammar-token-type">type </strong> ::= <a class="reference internal" href="#grammar-token-int-type"><code class="xref docutils literal notranslate"><span class="pre">int_type</span></code></a> | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "L" | "p" | "s"
|
||||
<strong id="grammar-token-int-type">int_type </strong> ::= "b" | "B" | "d" | "o" | "x" | "X"
|
||||
<strong id="grammar-token-width">width </strong> ::= <a class="reference internal" href="#grammar-token-integer"><code class="xref docutils literal"><span class="pre">integer</span></code></a> | "{" [<a class="reference internal" href="#grammar-token-arg_id"><code class="xref docutils literal"><span class="pre">arg_id</span></code></a>] "}"
|
||||
<strong id="grammar-token-precision">precision </strong> ::= <a class="reference internal" href="#grammar-token-integer"><code class="xref docutils literal"><span class="pre">integer</span></code></a> | "{" [<a class="reference internal" href="#grammar-token-arg_id"><code class="xref docutils literal"><span class="pre">arg_id</span></code></a>] "}"
|
||||
<strong id="grammar-token-type">type </strong> ::= <a class="reference internal" href="#grammar-token-int_type"><code class="xref docutils literal"><span class="pre">int_type</span></code></a> | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "L" | "p" | "s"
|
||||
<strong id="grammar-token-int_type">int_type </strong> ::= "b" | "B" | "d" | "o" | "x" | "X"
|
||||
</pre>
|
||||
<p>The <em>fill</em> character can be any Unicode code point other than <code class="docutils literal notranslate"><span class="pre">'{'</span></code> or
|
||||
<code class="docutils literal notranslate"><span class="pre">'}'</span></code>. The presence of a fill character is signaled by the character following
|
||||
<p>The <em>fill</em> character can be any Unicode code point other than <code class="docutils literal"><span class="pre">'{'</span></code> or
|
||||
<code class="docutils literal"><span class="pre">'}'</span></code>. The presence of a fill character is signaled by the character following
|
||||
it, which must be one of the alignment options. If the second character of
|
||||
<em>format_spec</em> is not a valid alignment option, then it is assumed that both the
|
||||
fill character and the alignment option are absent.</p>
|
||||
|
@ -208,15 +205,15 @@ fill character and the alignment option are absent.</p>
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'<'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'<'</span></code></td>
|
||||
<td>Forces the field to be left-aligned within the available
|
||||
space (this is the default for most objects).</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">'>'</span></code></td>
|
||||
<tr class="row-odd"><td><code class="docutils literal"><span class="pre">'>'</span></code></td>
|
||||
<td>Forces the field to be right-aligned within the
|
||||
available space (this is the default for numbers).</td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'^'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'^'</span></code></td>
|
||||
<td>Forces the field to be centered within the available
|
||||
space.</td>
|
||||
</tr>
|
||||
|
@ -238,11 +235,11 @@ following:</p>
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'+'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'+'</span></code></td>
|
||||
<td>indicates that a sign should be used for both
|
||||
positive as well as negative numbers.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">'-'</span></code></td>
|
||||
<tr class="row-odd"><td><code class="docutils literal"><span class="pre">'-'</span></code></td>
|
||||
<td>indicates that a sign should be used only for negative
|
||||
numbers (this is the default behavior).</td>
|
||||
</tr>
|
||||
|
@ -252,31 +249,31 @@ positive numbers, and a minus sign on negative numbers.</td>
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>The <code class="docutils literal notranslate"><span class="pre">'#'</span></code> option causes the “alternate form” to be used for the
|
||||
<p>The <code class="docutils literal"><span class="pre">'#'</span></code> option causes the “alternate form” to be used for the
|
||||
conversion. The alternate form is defined differently for different
|
||||
types. This option is only valid for integer and floating-point types.
|
||||
For integers, when binary, octal, or hexadecimal output is used, this
|
||||
option adds the prefix respective <code class="docutils literal notranslate"><span class="pre">"0b"</span></code> (<code class="docutils literal notranslate"><span class="pre">"0B"</span></code>), <code class="docutils literal notranslate"><span class="pre">"0"</span></code>, or
|
||||
<code class="docutils literal notranslate"><span class="pre">"0x"</span></code> (<code class="docutils literal notranslate"><span class="pre">"0X"</span></code>) to the output value. Whether the prefix is
|
||||
option adds the prefix respective <code class="docutils literal"><span class="pre">"0b"</span></code> (<code class="docutils literal"><span class="pre">"0B"</span></code>), <code class="docutils literal"><span class="pre">"0"</span></code>, or
|
||||
<code class="docutils literal"><span class="pre">"0x"</span></code> (<code class="docutils literal"><span class="pre">"0X"</span></code>) to the output value. Whether the prefix is
|
||||
lower-case or upper-case is determined by the case of the type
|
||||
specifier, for example, the prefix <code class="docutils literal notranslate"><span class="pre">"0x"</span></code> is used for the type <code class="docutils literal notranslate"><span class="pre">'x'</span></code>
|
||||
and <code class="docutils literal notranslate"><span class="pre">"0X"</span></code> is used for <code class="docutils literal notranslate"><span class="pre">'X'</span></code>. For floating-point numbers the
|
||||
specifier, for example, the prefix <code class="docutils literal"><span class="pre">"0x"</span></code> is used for the type <code class="docutils literal"><span class="pre">'x'</span></code>
|
||||
and <code class="docutils literal"><span class="pre">"0X"</span></code> is used for <code class="docutils literal"><span class="pre">'X'</span></code>. For floating-point numbers the
|
||||
alternate form causes the result of the conversion to always contain a
|
||||
decimal-point character, even if no digits follow it. Normally, a
|
||||
decimal-point character appears in the result of these conversions
|
||||
only if a digit follows it. In addition, for <code class="docutils literal notranslate"><span class="pre">'g'</span></code> and <code class="docutils literal notranslate"><span class="pre">'G'</span></code>
|
||||
only if a digit follows it. In addition, for <code class="docutils literal"><span class="pre">'g'</span></code> and <code class="docutils literal"><span class="pre">'G'</span></code>
|
||||
conversions, trailing zeros are not removed from the result.</p>
|
||||
<p><em>width</em> is a decimal integer defining the minimum field width. If not
|
||||
specified, then the field width will be determined by the content.</p>
|
||||
<p>Preceding the <em>width</em> field by a zero (<code class="docutils literal notranslate"><span class="pre">'0'</span></code>) character enables sign-aware
|
||||
<p>Preceding the <em>width</em> field by a zero (<code class="docutils literal"><span class="pre">'0'</span></code>) character enables sign-aware
|
||||
zero-padding for numeric types. It forces the padding to be placed after the
|
||||
sign or base (if any) but before the digits. This is used for printing fields in
|
||||
the form ‘+000000120’. This option is only valid for numeric types and it has no
|
||||
the form ‘+000000120’. This option is only valid for numeric types and it has no
|
||||
effect on formatting of infinity and NaN.</p>
|
||||
<p>The <em>precision</em> is a decimal number indicating how many digits should be
|
||||
displayed after the decimal point for a floating-point value formatted with
|
||||
<code class="docutils literal notranslate"><span class="pre">'f'</span></code> and <code class="docutils literal notranslate"><span class="pre">'F'</span></code>, or before and after the decimal point for a floating-point
|
||||
value formatted with <code class="docutils literal notranslate"><span class="pre">'g'</span></code> or <code class="docutils literal notranslate"><span class="pre">'G'</span></code>. For non-number types the field
|
||||
<code class="docutils literal"><span class="pre">'f'</span></code> and <code class="docutils literal"><span class="pre">'F'</span></code>, or before and after the decimal point for a floating-point
|
||||
value formatted with <code class="docutils literal"><span class="pre">'g'</span></code> or <code class="docutils literal"><span class="pre">'G'</span></code>. For non-number types the field
|
||||
indicates the maximum field size - in other words, how many characters will be
|
||||
used from the field content. The <em>precision</em> is not allowed for integer,
|
||||
character, Boolean, and pointer values.</p>
|
||||
|
@ -293,12 +290,12 @@ character, Boolean, and pointer values.</p>
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'s'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'s'</span></code></td>
|
||||
<td>String format. This is the default type for strings and
|
||||
may be omitted.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td>none</td>
|
||||
<td>The same as <code class="docutils literal notranslate"><span class="pre">'s'</span></code>.</td>
|
||||
<td>The same as <code class="docutils literal"><span class="pre">'s'</span></code>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -314,12 +311,12 @@ may be omitted.</td>
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'c'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'c'</span></code></td>
|
||||
<td>Character format. This is the default type for
|
||||
characters and may be omitted.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td>none</td>
|
||||
<td>The same as <code class="docutils literal notranslate"><span class="pre">'c'</span></code>.</td>
|
||||
<td>The same as <code class="docutils literal"><span class="pre">'c'</span></code>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -335,47 +332,47 @@ characters and may be omitted.</td>
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'b'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'b'</span></code></td>
|
||||
<td>Binary format. Outputs the number in base 2. Using the
|
||||
<code class="docutils literal notranslate"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal notranslate"><span class="pre">"0b"</span></code>
|
||||
<code class="docutils literal"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal"><span class="pre">"0b"</span></code>
|
||||
to the output value.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">'B'</span></code></td>
|
||||
<tr class="row-odd"><td><code class="docutils literal"><span class="pre">'B'</span></code></td>
|
||||
<td>Binary format. Outputs the number in base 2. Using the
|
||||
<code class="docutils literal notranslate"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal notranslate"><span class="pre">"0B"</span></code>
|
||||
<code class="docutils literal"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal"><span class="pre">"0B"</span></code>
|
||||
to the output value.</td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'d'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'d'</span></code></td>
|
||||
<td>Decimal integer. Outputs the number in base 10.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">'o'</span></code></td>
|
||||
<tr class="row-odd"><td><code class="docutils literal"><span class="pre">'o'</span></code></td>
|
||||
<td>Octal format. Outputs the number in base 8.</td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'x'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'x'</span></code></td>
|
||||
<td>Hex format. Outputs the number in base 16, using
|
||||
lower-case letters for the digits above 9. Using the
|
||||
<code class="docutils literal notranslate"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal notranslate"><span class="pre">"0x"</span></code>
|
||||
<code class="docutils literal"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal"><span class="pre">"0x"</span></code>
|
||||
to the output value.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">'X'</span></code></td>
|
||||
<tr class="row-odd"><td><code class="docutils literal"><span class="pre">'X'</span></code></td>
|
||||
<td>Hex format. Outputs the number in base 16, using
|
||||
upper-case letters for the digits above 9. Using the
|
||||
<code class="docutils literal notranslate"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal notranslate"><span class="pre">"0X"</span></code>
|
||||
<code class="docutils literal"><span class="pre">'#'</span></code> option with this type adds the prefix <code class="docutils literal"><span class="pre">"0X"</span></code>
|
||||
to the output value.</td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'L'</span></code></td>
|
||||
<td>Locale-specific format. This is the same as <code class="docutils literal notranslate"><span class="pre">'d'</span></code>,
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'L'</span></code></td>
|
||||
<td>Locale-specific format. This is the same as <code class="docutils literal"><span class="pre">'d'</span></code>,
|
||||
except that it uses the current locale setting to insert
|
||||
the appropriate number separator characters.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td>none</td>
|
||||
<td>The same as <code class="docutils literal notranslate"><span class="pre">'d'</span></code>.</td>
|
||||
<td>The same as <code class="docutils literal"><span class="pre">'d'</span></code>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Integer presentation types can also be used with character and Boolean values.
|
||||
Boolean values are formatted using textual representation, either <code class="docutils literal notranslate"><span class="pre">true</span></code> or
|
||||
<code class="docutils literal notranslate"><span class="pre">false</span></code>, if the presentation type is not specified.</p>
|
||||
Boolean values are formatted using textual representation, either <code class="docutils literal"><span class="pre">true</span></code> or
|
||||
<code class="docutils literal"><span class="pre">false</span></code>, if the presentation type is not specified.</p>
|
||||
<p>The available presentation types for floating-point values are:</p>
|
||||
<table border="1" class="docutils">
|
||||
<colgroup>
|
||||
|
@ -388,52 +385,52 @@ Boolean values are formatted using textual representation, either <code class="d
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'a'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'a'</span></code></td>
|
||||
<td>Hexadecimal floating point format. Prints the number in
|
||||
base 16 with prefix <code class="docutils literal notranslate"><span class="pre">"0x"</span></code> and lower-case letters for
|
||||
digits above 9. Uses <code class="docutils literal notranslate"><span class="pre">'p'</span></code> to indicate the exponent.</td>
|
||||
base 16 with prefix <code class="docutils literal"><span class="pre">"0x"</span></code> and lower-case letters for
|
||||
digits above 9. Uses <code class="docutils literal"><span class="pre">'p'</span></code> to indicate the exponent.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">'A'</span></code></td>
|
||||
<td>Same as <code class="docutils literal notranslate"><span class="pre">'a'</span></code> except it uses upper-case letters for
|
||||
<tr class="row-odd"><td><code class="docutils literal"><span class="pre">'A'</span></code></td>
|
||||
<td>Same as <code class="docutils literal"><span class="pre">'a'</span></code> except it uses upper-case letters for
|
||||
the prefix, digits above 9 and to indicate the exponent.</td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'e'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'e'</span></code></td>
|
||||
<td>Exponent notation. Prints the number in scientific
|
||||
notation using the letter ‘e’ to indicate the exponent.</td>
|
||||
notation using the letter ‘e’ to indicate the exponent.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">'E'</span></code></td>
|
||||
<td>Exponent notation. Same as <code class="docutils literal notranslate"><span class="pre">'e'</span></code> except it uses an
|
||||
upper-case <code class="docutils literal notranslate"><span class="pre">'E'</span></code> as the separator character.</td>
|
||||
<tr class="row-odd"><td><code class="docutils literal"><span class="pre">'E'</span></code></td>
|
||||
<td>Exponent notation. Same as <code class="docutils literal"><span class="pre">'e'</span></code> except it uses an
|
||||
upper-case <code class="docutils literal"><span class="pre">'E'</span></code> as the separator character.</td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'f'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'f'</span></code></td>
|
||||
<td>Fixed point. Displays the number as a fixed-point
|
||||
number.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">'F'</span></code></td>
|
||||
<td>Fixed point. Same as <code class="docutils literal notranslate"><span class="pre">'f'</span></code>, but converts <code class="docutils literal notranslate"><span class="pre">nan</span></code> to
|
||||
<code class="docutils literal notranslate"><span class="pre">NAN</span></code> and <code class="docutils literal notranslate"><span class="pre">inf</span></code> to <code class="docutils literal notranslate"><span class="pre">INF</span></code>.</td>
|
||||
<tr class="row-odd"><td><code class="docutils literal"><span class="pre">'F'</span></code></td>
|
||||
<td>Fixed point. Same as <code class="docutils literal"><span class="pre">'f'</span></code>, but converts <code class="docutils literal"><span class="pre">nan</span></code> to
|
||||
<code class="docutils literal"><span class="pre">NAN</span></code> and <code class="docutils literal"><span class="pre">inf</span></code> to <code class="docutils literal"><span class="pre">INF</span></code>.</td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'g'</span></code></td>
|
||||
<td><p class="first">General format. For a given precision <code class="docutils literal notranslate"><span class="pre">p</span> <span class="pre">>=</span> <span class="pre">1</span></code>,
|
||||
this rounds the number to <code class="docutils literal notranslate"><span class="pre">p</span></code> significant digits and
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'g'</span></code></td>
|
||||
<td><p class="first">General format. For a given precision <code class="docutils literal"><span class="pre">p</span> <span class="pre">>=</span> <span class="pre">1</span></code>,
|
||||
this rounds the number to <code class="docutils literal"><span class="pre">p</span></code> significant digits and
|
||||
then formats the result in either fixed-point format
|
||||
or in scientific notation, depending on its magnitude.</p>
|
||||
<p class="last">A precision of <code class="docutils literal notranslate"><span class="pre">0</span></code> is treated as equivalent to a
|
||||
precision of <code class="docutils literal notranslate"><span class="pre">1</span></code>.</p>
|
||||
<p class="last">A precision of <code class="docutils literal"><span class="pre">0</span></code> is treated as equivalent to a
|
||||
precision of <code class="docutils literal"><span class="pre">1</span></code>.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td><code class="docutils literal notranslate"><span class="pre">'G'</span></code></td>
|
||||
<td>General format. Same as <code class="docutils literal notranslate"><span class="pre">'g'</span></code> except switches to
|
||||
<code class="docutils literal notranslate"><span class="pre">'E'</span></code> if the number gets too large. The
|
||||
<tr class="row-odd"><td><code class="docutils literal"><span class="pre">'G'</span></code></td>
|
||||
<td>General format. Same as <code class="docutils literal"><span class="pre">'g'</span></code> except switches to
|
||||
<code class="docutils literal"><span class="pre">'E'</span></code> if the number gets too large. The
|
||||
representations of infinity and NaN are uppercased, too.</td>
|
||||
</tr>
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'L'</span></code></td>
|
||||
<td>Locale-specific format. This is the same as <code class="docutils literal notranslate"><span class="pre">'g'</span></code>,
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'L'</span></code></td>
|
||||
<td>Locale-specific format. This is the same as <code class="docutils literal"><span class="pre">'g'</span></code>,
|
||||
except that it uses the current locale setting to insert
|
||||
the appropriate number separator characters.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td>none</td>
|
||||
<td>Similar to <code class="docutils literal notranslate"><span class="pre">'g'</span></code>, except that fixed-point notation,
|
||||
<td>Similar to <code class="docutils literal"><span class="pre">'g'</span></code>, except that fixed-point notation,
|
||||
when used, has at least one digit past the decimal
|
||||
point. The default precision is as high as needed to
|
||||
represent the particular value.</td>
|
||||
|
@ -452,12 +449,12 @@ represent the particular value.</td>
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody valign="top">
|
||||
<tr class="row-even"><td><code class="docutils literal notranslate"><span class="pre">'p'</span></code></td>
|
||||
<tr class="row-even"><td><code class="docutils literal"><span class="pre">'p'</span></code></td>
|
||||
<td>Pointer format. This is the default type for
|
||||
pointers and may be omitted.</td>
|
||||
</tr>
|
||||
<tr class="row-odd"><td>none</td>
|
||||
<td>The same as <code class="docutils literal notranslate"><span class="pre">'p'</span></code>.</td>
|
||||
<td>The same as <code class="docutils literal"><span class="pre">'p'</span></code>.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -467,12 +464,12 @@ pointers and may be omitted.</td>
|
|||
<p>This section contains examples of the format syntax and comparison with
|
||||
the printf formatting.</p>
|
||||
<p>In most of the cases the syntax is similar to the printf formatting, with the
|
||||
addition of the <code class="docutils literal notranslate"><span class="pre">{}</span></code> and with <code class="docutils literal notranslate"><span class="pre">:</span></code> used instead of <code class="docutils literal notranslate"><span class="pre">%</span></code>.
|
||||
For example, <code class="docutils literal notranslate"><span class="pre">"%03.2f"</span></code> can be translated to <code class="docutils literal notranslate"><span class="pre">"{:03.2f}"</span></code>.</p>
|
||||
addition of the <code class="docutils literal"><span class="pre">{}</span></code> and with <code class="docutils literal"><span class="pre">:</span></code> used instead of <code class="docutils literal"><span class="pre">%</span></code>.
|
||||
For example, <code class="docutils literal"><span class="pre">"%03.2f"</span></code> can be translated to <code class="docutils literal"><span class="pre">"{:03.2f}"</span></code>.</p>
|
||||
<p>The new format syntax also supports new and different options, shown in the
|
||||
following examples.</p>
|
||||
<p>Accessing arguments by position:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{0}, {1}, {2}"</span><span class="p">,</span> <span class="sc">'a'</span><span class="p">,</span> <span class="sc">'b'</span><span class="p">,</span> <span class="sc">'c'</span><span class="p">);</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{0}, {1}, {2}"</span><span class="p">,</span> <span class="sc">'a'</span><span class="p">,</span> <span class="sc">'b'</span><span class="p">,</span> <span class="sc">'c'</span><span class="p">);</span>
|
||||
<span class="c1">// Result: "a, b, c"</span>
|
||||
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{}, {}, {}"</span><span class="p">,</span> <span class="sc">'a'</span><span class="p">,</span> <span class="sc">'b'</span><span class="p">,</span> <span class="sc">'c'</span><span class="p">);</span>
|
||||
<span class="c1">// Result: "a, b, c"</span>
|
||||
|
@ -483,7 +480,7 @@ following examples.</p>
|
|||
</pre></div>
|
||||
</div>
|
||||
<p>Aligning the text and specifying a width:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{:<30}"</span><span class="p">,</span> <span class="s">"left aligned"</span><span class="p">);</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{:<30}"</span><span class="p">,</span> <span class="s">"left aligned"</span><span class="p">);</span>
|
||||
<span class="c1">// Result: "left aligned "</span>
|
||||
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{:>30}"</span><span class="p">,</span> <span class="s">"right aligned"</span><span class="p">);</span>
|
||||
<span class="c1">// Result: " right aligned"</span>
|
||||
|
@ -494,17 +491,17 @@ following examples.</p>
|
|||
</pre></div>
|
||||
</div>
|
||||
<p>Dynamic width:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{:<{}}"</span><span class="p">,</span> <span class="s">"left aligned"</span><span class="p">,</span> <span class="mi">30</span><span class="p">);</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{:<{}}"</span><span class="p">,</span> <span class="s">"left aligned"</span><span class="p">,</span> <span class="mi">30</span><span class="p">);</span>
|
||||
<span class="c1">// Result: "left aligned "</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Dynamic precision:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{:.{}f}"</span><span class="p">,</span> <span class="mf">3.14</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{:.{}f}"</span><span class="p">,</span> <span class="mf">3.14</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
|
||||
<span class="c1">// Result: "3.1"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Replacing <code class="docutils literal notranslate"><span class="pre">%+f</span></code>, <code class="docutils literal notranslate"><span class="pre">%-f</span></code>, and <code class="docutils literal notranslate"><span class="pre">%</span> <span class="pre">f</span></code> and specifying a sign:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{:+f}; {:+f}"</span><span class="p">,</span> <span class="mf">3.14</span><span class="p">,</span> <span class="o">-</span><span class="mf">3.14</span><span class="p">);</span> <span class="c1">// show it always</span>
|
||||
<p>Replacing <code class="docutils literal"><span class="pre">%+f</span></code>, <code class="docutils literal"><span class="pre">%-f</span></code>, and <code class="docutils literal"><span class="pre">%</span> <span class="pre">f</span></code> and specifying a sign:</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{:+f}; {:+f}"</span><span class="p">,</span> <span class="mf">3.14</span><span class="p">,</span> <span class="o">-</span><span class="mf">3.14</span><span class="p">);</span> <span class="c1">// show it always</span>
|
||||
<span class="c1">// Result: "+3.140000; -3.140000"</span>
|
||||
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{: f}; {: f}"</span><span class="p">,</span> <span class="mf">3.14</span><span class="p">,</span> <span class="o">-</span><span class="mf">3.14</span><span class="p">);</span> <span class="c1">// show a space for positive numbers</span>
|
||||
<span class="c1">// Result: " 3.140000; -3.140000"</span>
|
||||
|
@ -512,8 +509,8 @@ following examples.</p>
|
|||
<span class="c1">// Result: "3.140000; -3.140000"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Replacing <code class="docutils literal notranslate"><span class="pre">%x</span></code> and <code class="docutils literal notranslate"><span class="pre">%o</span></code> and converting the value to different bases:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}"</span><span class="p">,</span> <span class="mi">42</span><span class="p">);</span>
|
||||
<p>Replacing <code class="docutils literal"><span class="pre">%x</span></code> and <code class="docutils literal"><span class="pre">%o</span></code> and converting the value to different bases:</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}"</span><span class="p">,</span> <span class="mi">42</span><span class="p">);</span>
|
||||
<span class="c1">// Result: "int: 42; hex: 2a; oct: 52; bin: 101010"</span>
|
||||
<span class="c1">// with 0x or 0 or 0b as prefix:</span>
|
||||
<span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}"</span><span class="p">,</span> <span class="mi">42</span><span class="p">);</span>
|
||||
|
@ -521,25 +518,25 @@ following examples.</p>
|
|||
</pre></div>
|
||||
</div>
|
||||
<p>Padded hex byte with prefix and always prints both hex characters:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{:#04x}"</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="s">"{:#04x}"</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
|
||||
<span class="c1">// Result: "0x00"</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Box drawing using Unicode fill:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">fmt</span><span class="o">::</span><span class="n">print</span><span class="p">(</span>
|
||||
<span class="s">"┌{0:─^{2}}┐</span><span class="se">\n</span><span class="s">"</span>
|
||||
<span class="s">"│{1: ^{2}}│</span><span class="se">\n</span><span class="s">"</span>
|
||||
<span class="s">"└{0:─^{2}}┘</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="s">""</span><span class="p">,</span> <span class="s">"Hello, world!"</span><span class="p">,</span> <span class="mi">20</span><span class="p">);</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>prints:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span>┌────────────────────┐
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span>┌────────────────────┐
|
||||
│ Hello, world! │
|
||||
└────────────────────┘
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Using type-specific formatting:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><fmt/chrono.h></span><span class="cp"></span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><fmt/chrono.h></span><span class="cp"></span>
|
||||
|
||||
<span class="k">auto</span> <span class="n">t</span> <span class="o">=</span> <span class="n">tm</span><span class="p">();</span>
|
||||
<span class="n">t</span><span class="p">.</span><span class="n">tm_year</span> <span class="o">=</span> <span class="mi">2010</span> <span class="o">-</span> <span class="mi">1900</span><span class="p">;</span>
|
||||
|
@ -553,7 +550,7 @@ following examples.</p>
|
|||
</pre></div>
|
||||
</div>
|
||||
<p>Using the comma as a thousands separator:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><fmt/locale.h></span><span class="cp"></span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="cp">#include</span> <span class="cpf"><fmt/locale.h></span><span class="cp"></span>
|
||||
|
||||
<span class="k">auto</span> <span class="n">s</span> <span class="o">=</span> <span class="n">fmt</span><span class="o">::</span><span class="n">format</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">locale</span><span class="p">(</span><span class="s">"en_US.UTF-8"</span><span class="p">),</span> <span class="s">"{:L}"</span><span class="p">,</span> <span class="mi">1234567890</span><span class="p">);</span>
|
||||
<span class="c1">// s == "1,234,567,890"</span>
|
||||
|
@ -571,7 +568,7 @@ following examples.</p>
|
|||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2012-present, Victor Zverovich.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.8.5.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.4.1+.
|
||||
</div>
|
||||
|
||||
<script src="_static/bootstrap.min.js"></script>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
|
||||
<title>Usage — fmt 7.0.3 documentation</title>
|
||||
<title>Usage — fmt 7.1.3 documentation</title>
|
||||
|
||||
<link rel="stylesheet" href="_static/basic.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
@ -17,19 +17,16 @@
|
|||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: './',
|
||||
VERSION: '7.0.3',
|
||||
VERSION: '7.1.3',
|
||||
COLLAPSE_INDEX: false,
|
||||
FILE_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt'
|
||||
SOURCELINK_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/underscore.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<script type="text/javascript" src="_static/language_data.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="API Reference" href="api.html" />
|
||||
<link rel="prev" title="Contents" href="contents.html" />
|
||||
<meta name="description" content="Small, safe and fast formatting library">
|
||||
|
@ -69,15 +66,15 @@
|
|||
<ul class="nav navbar-nav">
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"
|
||||
role="button" aria-expanded="false">7.0.3
|
||||
role="button" aria-expanded="false">7.1.3
|
||||
<span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.3">7.0.3</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.3">7.1.3</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.2">7.0.2</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.2">7.1.2</a></li>
|
||||
|
||||
<li><a href="https://fmt.dev/7.0.1">7.0.1</a></li>
|
||||
<li><a href="https://fmt.dev/7.1.1">7.1.1</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
@ -129,8 +126,8 @@
|
|||
|
||||
<div class="section" id="usage">
|
||||
<h1>Usage<a class="headerlink" href="#usage" title="Permalink to this headline">¶</a></h1>
|
||||
<p>To use the {fmt} library, add <code class="file docutils literal notranslate"><span class="pre">fmt/core.h</span></code>, <code class="file docutils literal notranslate"><span class="pre">fmt/format.h</span></code>,
|
||||
<code class="file docutils literal notranslate"><span class="pre">fmt/format-inl.h</span></code>, <code class="file docutils literal notranslate"><span class="pre">src/format.cc</span></code> and optionally other headers
|
||||
<p>To use the {fmt} library, add <code class="file docutils literal"><span class="pre">fmt/core.h</span></code>, <code class="file docutils literal"><span class="pre">fmt/format.h</span></code>,
|
||||
<code class="file docutils literal"><span class="pre">fmt/format-inl.h</span></code>, <code class="file docutils literal"><span class="pre">src/format.cc</span></code> and optionally other headers
|
||||
from a <a class="reference external" href="https://github.com/fmtlib/fmt/releases/latest">release archive</a> or
|
||||
the <a class="reference external" href="https://github.com/fmtlib/fmt">Git repository</a> to your project.
|
||||
Alternatively, you can <a class="reference internal" href="#building"><span class="std std-ref">build the library with CMake</span></a>.</p>
|
||||
|
@ -142,26 +139,32 @@ download from <a class="reference external" href="http://www.cmake.org/download/
|
|||
<p>CMake works by generating native makefiles or project files that can
|
||||
be used in the compiler environment of your choice. The typical
|
||||
workflow starts with:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span>mkdir build # Create a directory to hold the build output.
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span>mkdir build # Create a directory to hold the build output.
|
||||
cd build
|
||||
cmake .. # Generate native build scripts.
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>where <code class="file docutils literal notranslate"><em><span class="pre"><path/to/fmt></span></em></code> is a path to the <code class="docutils literal notranslate"><span class="pre">fmt</span></code> repository.</p>
|
||||
<p>where <code class="file docutils literal"><em><span class="pre"><path/to/fmt></span></em></code> is a path to the <code class="docutils literal"><span class="pre">fmt</span></code> repository.</p>
|
||||
<p>If you are on a *nix system, you should now see a Makefile in the
|
||||
current directory. Now you can build the library by running <strong class="command">make</strong>.</p>
|
||||
<p>Once the library has been built you can invoke <strong class="command">make test</strong> to run
|
||||
the tests.</p>
|
||||
<p>You can control generation of the make <code class="docutils literal notranslate"><span class="pre">test</span></code> target with the <code class="docutils literal notranslate"><span class="pre">FMT_TEST</span></code>
|
||||
<p>You can control generation of the make <code class="docutils literal"><span class="pre">test</span></code> target with the <code class="docutils literal"><span class="pre">FMT_TEST</span></code>
|
||||
CMake option. This can be useful if you include fmt as a subdirectory in
|
||||
your project but don’t want to add fmt’s tests to your <code class="docutils literal notranslate"><span class="pre">test</span></code> target.</p>
|
||||
<p>If you use Windows and have Visual Studio installed, a <code class="file docutils literal notranslate"><span class="pre">FMT.sln</span></code>
|
||||
file and several <code class="file docutils literal notranslate"><span class="pre">.vcproj</span></code> files will be created. You can then build them
|
||||
your project but don’t want to add fmt’s tests to your <code class="docutils literal"><span class="pre">test</span></code> target.</p>
|
||||
<p>If you use Windows and have Visual Studio installed, a <code class="file docutils literal"><span class="pre">FMT.sln</span></code>
|
||||
file and several <code class="file docutils literal"><span class="pre">.vcproj</span></code> files will be created. You can then build them
|
||||
using Visual Studio or msbuild.</p>
|
||||
<p>On Mac OS X with Xcode installed, an <code class="file docutils literal notranslate"><span class="pre">.xcodeproj</span></code> file will be generated.</p>
|
||||
<p>To build a <a class="reference external" href="http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries">shared library</a> set the <code class="docutils literal notranslate"><span class="pre">BUILD_SHARED_LIBS</span></code> CMake variable to
|
||||
<code class="docutils literal notranslate"><span class="pre">TRUE</span></code>:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">cmake</span> <span class="o">-</span><span class="n">DBUILD_SHARED_LIBS</span><span class="o">=</span><span class="n">TRUE</span> <span class="p">...</span>
|
||||
<p>On Mac OS X with Xcode installed, an <code class="file docutils literal"><span class="pre">.xcodeproj</span></code> file will be generated.</p>
|
||||
<p>To build a <a class="reference external" href="http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries">shared library</a> set the <code class="docutils literal"><span class="pre">BUILD_SHARED_LIBS</span></code> CMake variable to
|
||||
<code class="docutils literal"><span class="pre">TRUE</span></code>:</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">cmake</span> <span class="o">-</span><span class="n">DBUILD_SHARED_LIBS</span><span class="o">=</span><span class="n">TRUE</span> <span class="p">...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>To build a <code class="xref cpp cpp-any docutils literal"><span class="pre">static</span> <span class="pre">library</span></code> with position independent code (required if the main
|
||||
consumer of the fmt library is a shared library i.e. a Python extension) set the
|
||||
<code class="docutils literal"><span class="pre">CMAKE_POSITION_INDEPENDENT_CODE</span></code> CMake variable to <code class="docutils literal"><span class="pre">TRUE</span></code>:</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">cmake</span> <span class="o">-</span><span class="n">DCMAKE_POSITION_INDEPENDENT_CODE</span><span class="o">=</span><span class="n">TRUE</span> <span class="p">...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -172,26 +175,70 @@ using Visual Studio or msbuild.</p>
|
|||
</div>
|
||||
<div class="section" id="usage-with-cmake">
|
||||
<h2>Usage with CMake<a class="headerlink" href="#usage-with-cmake" title="Permalink to this headline">¶</a></h2>
|
||||
<p>You can add the <code class="docutils literal notranslate"><span class="pre">fmt</span></code> library directory into your project and include it in
|
||||
your <code class="docutils literal notranslate"><span class="pre">CMakeLists.txt</span></code> file:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">add_subdirectory</span><span class="p">(</span><span class="n">fmt</span><span class="p">)</span>
|
||||
<p>You can add the <code class="docutils literal"><span class="pre">fmt</span></code> library directory into your project and include it in
|
||||
your <code class="docutils literal"><span class="pre">CMakeLists.txt</span></code> file:</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">add_subdirectory</span><span class="p">(</span><span class="n">fmt</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>or</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">add_subdirectory</span><span class="p">(</span><span class="n">fmt</span> <span class="n">EXCLUDE_FROM_ALL</span><span class="p">)</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">add_subdirectory</span><span class="p">(</span><span class="n">fmt</span> <span class="n">EXCLUDE_FROM_ALL</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>to exclude it from <code class="docutils literal notranslate"><span class="pre">make</span></code>, <code class="docutils literal notranslate"><span class="pre">make</span> <span class="pre">all</span></code>, or <code class="docutils literal notranslate"><span class="pre">cmake</span> <span class="pre">--build</span> <span class="pre">.</span></code>.</p>
|
||||
<p>to exclude it from <code class="docutils literal"><span class="pre">make</span></code>, <code class="docutils literal"><span class="pre">make</span> <span class="pre">all</span></code>, or <code class="docutils literal"><span class="pre">cmake</span> <span class="pre">--build</span> <span class="pre">.</span></code>.</p>
|
||||
<p>You can detect and use an installed version of {fmt} as follows:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">find_package</span><span class="p">(</span><span class="n">fmt</span><span class="p">)</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">find_package</span><span class="p">(</span><span class="n">fmt</span><span class="p">)</span>
|
||||
<span class="n">target_link_libraries</span><span class="p">(</span><span class="o"><</span><span class="n">your</span><span class="o">-</span><span class="n">target</span><span class="o">></span> <span class="n">fmt</span><span class="o">::</span><span class="n">fmt</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Setting up your target to use a header-only version of <code class="docutils literal notranslate"><span class="pre">fmt</span></code> is equally easy:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">target_link_libraries</span><span class="p">(</span><span class="o"><</span><span class="n">your</span><span class="o">-</span><span class="n">target</span><span class="o">></span> <span class="n">PRIVATE</span> <span class="n">fmt</span><span class="o">::</span><span class="n">fmt</span><span class="o">-</span><span class="n">header</span><span class="o">-</span><span class="n">only</span><span class="p">)</span>
|
||||
<p>Setting up your target to use a header-only version of <code class="docutils literal"><span class="pre">fmt</span></code> is equally easy:</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">target_link_libraries</span><span class="p">(</span><span class="o"><</span><span class="n">your</span><span class="o">-</span><span class="n">target</span><span class="o">></span> <span class="n">PRIVATE</span> <span class="n">fmt</span><span class="o">::</span><span class="n">fmt</span><span class="o">-</span><span class="n">header</span><span class="o">-</span><span class="n">only</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="usage-with-build2">
|
||||
<h2>Usage with build2<a class="headerlink" href="#usage-with-build2" title="Permalink to this headline">¶</a></h2>
|
||||
<p>You can use <a class="reference external" href="https://build2.org">build2</a>, a dependency manager and a
|
||||
build-system combined, to use <code class="docutils literal"><span class="pre">fmt</span></code>.</p>
|
||||
<p>Currently this package is available in these package repositories:</p>
|
||||
<ul class="simple">
|
||||
<li><strong>https://cppget.org/fmt/</strong> for released and published versions.</li>
|
||||
<li><a class="reference external" href="https://github.com/build2-packaging/fmt.git">The git repository with the sources of the build2 package of fmt</a>
|
||||
for unreleased or custom revisions of <code class="docutils literal"><span class="pre">fmt</span></code>.</li>
|
||||
</ul>
|
||||
<p><strong>Usage:</strong></p>
|
||||
<ul class="simple">
|
||||
<li><code class="docutils literal"><span class="pre">build2</span></code> package name: <code class="docutils literal"><span class="pre">fmt</span></code></li>
|
||||
<li>Library target name : <code class="docutils literal"><span class="pre">lib{fmt}</span></code></li>
|
||||
</ul>
|
||||
<p>For example, to make your <code class="docutils literal"><span class="pre">build2</span></code> project depend on <code class="docutils literal"><span class="pre">fmt</span></code>:</p>
|
||||
<ul>
|
||||
<li><p class="first">Add one of the repositories to your configurations, or in your
|
||||
<code class="docutils literal"><span class="pre">repositories.manifest</span></code>, if not already there:</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="o">:</span>
|
||||
<span class="nl">role</span><span class="p">:</span> <span class="n">prerequisite</span>
|
||||
<span class="nl">location</span><span class="p">:</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//pkg.cppget.org/1/stable</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first">Add this package as a dependency to your <code class="docutils literal"><span class="pre">./manifest</span></code> file
|
||||
(example for <code class="docutils literal"><span class="pre">v7.0.x</span></code>):</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="nl">depends</span><span class="p">:</span> <span class="n">fmt</span> <span class="o">~</span><span class="mf">7.0.0</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
<li><p class="first">Import the target and use it as a prerequisite to your own target
|
||||
using <code class="xref cpp cpp-any docutils literal"><span class="pre">fmt</span></code> in the appropriate <code class="docutils literal"><span class="pre">buildfile</span></code>:</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span>import fmt = fmt%lib{fmt}
|
||||
lib{mylib} : cxx{**} ... $fmt
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Then build your project as usual with <code class="xref cpp cpp-any docutils literal"><span class="pre">b</span></code> or <code class="xref cpp cpp-any docutils literal"><span class="pre">bdep</span> <span class="pre">update</span></code>.</p>
|
||||
<p>For <code class="docutils literal"><span class="pre">build2</span></code> newcomers or to get more details and use cases, you can read the
|
||||
<code class="docutils literal"><span class="pre">build2</span></code>
|
||||
<a class="reference external" href="https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml">toolchain introduction</a>.</p>
|
||||
</div>
|
||||
<div class="section" id="building-the-documentation">
|
||||
<h2>Building the Documentation<a class="headerlink" href="#building-the-documentation" title="Permalink to this headline">¶</a></h2>
|
||||
<p>To build the documentation you need the following software installed on your
|
||||
|
@ -201,20 +248,20 @@ system:</p>
|
|||
</li>
|
||||
<li><p class="first"><a class="reference external" href="http://www.stack.nl/~dimitri/doxygen/">Doxygen</a></p>
|
||||
</li>
|
||||
<li><p class="first"><a class="reference external" href="http://lesscss.org/">Less</a> with <code class="docutils literal notranslate"><span class="pre">less-plugin-clean-css</span></code>.
|
||||
Ubuntu doesn’t package the <code class="docutils literal notranslate"><span class="pre">clean-css</span></code> plugin so you should use <code class="docutils literal notranslate"><span class="pre">npm</span></code>
|
||||
instead of <code class="docutils literal notranslate"><span class="pre">apt</span></code> to install both <code class="docutils literal notranslate"><span class="pre">less</span></code> and the plugin:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">sudo</span> <span class="n">npm</span> <span class="n">install</span> <span class="o">-</span><span class="n">g</span> <span class="n">less</span> <span class="n">less</span><span class="o">-</span><span class="n">plugin</span><span class="o">-</span><span class="n">clean</span><span class="o">-</span><span class="n">css</span><span class="p">.</span>
|
||||
<li><p class="first"><a class="reference external" href="http://lesscss.org/">Less</a> with <code class="docutils literal"><span class="pre">less-plugin-clean-css</span></code>.
|
||||
Ubuntu doesn’t package the <code class="docutils literal"><span class="pre">clean-css</span></code> plugin so you should use <code class="docutils literal"><span class="pre">npm</span></code>
|
||||
instead of <code class="docutils literal"><span class="pre">apt</span></code> to install both <code class="docutils literal"><span class="pre">less</span></code> and the plugin:</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">sudo</span> <span class="n">npm</span> <span class="n">install</span> <span class="o">-</span><span class="n">g</span> <span class="n">less</span> <span class="n">less</span><span class="o">-</span><span class="n">plugin</span><span class="o">-</span><span class="n">clean</span><span class="o">-</span><span class="n">css</span><span class="p">.</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<p>First generate makefiles or project files using CMake as described in
|
||||
the previous section. Then compile the <code class="docutils literal notranslate"><span class="pre">doc</span></code> target/project, for example:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">make</span> <span class="n">doc</span>
|
||||
the previous section. Then compile the <code class="docutils literal"><span class="pre">doc</span></code> target/project, for example:</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">make</span> <span class="n">doc</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This will generate the HTML documentation in <code class="docutils literal notranslate"><span class="pre">doc/html</span></code>.</p>
|
||||
<p>This will generate the HTML documentation in <code class="docutils literal"><span class="pre">doc/html</span></code>.</p>
|
||||
</div>
|
||||
<div class="section" id="conda">
|
||||
<h2>Conda<a class="headerlink" href="#conda" title="Permalink to this headline">¶</a></h2>
|
||||
|
@ -222,14 +269,14 @@ the previous section. Then compile the <code class="docutils literal notranslate
|
|||
<a class="reference external" href="https://docs.conda.io/en/latest/">Conda</a>, using its
|
||||
<a class="reference external" href="https://conda-forge.org">conda-forge</a>
|
||||
<a class="reference external" href="https://github.com/conda-forge/fmt-feedstock">package</a>, as follows:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">conda</span> <span class="n">install</span> <span class="o">-</span><span class="n">c</span> <span class="n">conda</span><span class="o">-</span><span class="n">forge</span> <span class="n">fmt</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">conda</span> <span class="n">install</span> <span class="o">-</span><span class="n">c</span> <span class="n">conda</span><span class="o">-</span><span class="n">forge</span> <span class="n">fmt</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="vcpkg">
|
||||
<h2>Vcpkg<a class="headerlink" href="#vcpkg" title="Permalink to this headline">¶</a></h2>
|
||||
<p>You can download and install fmt using the <a class="reference external" href="https://github.com/Microsoft/vcpkg">vcpkg</a> dependency manager:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">git</span> <span class="n">clone</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//github.com/Microsoft/vcpkg.git</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">git</span> <span class="n">clone</span> <span class="nl">https</span><span class="p">:</span><span class="c1">//github.com/Microsoft/vcpkg.git</span>
|
||||
<span class="n">cd</span> <span class="n">vcpkg</span>
|
||||
<span class="p">.</span><span class="o">/</span><span class="n">bootstrap</span><span class="o">-</span><span class="n">vcpkg</span><span class="p">.</span><span class="n">sh</span>
|
||||
<span class="p">.</span><span class="o">/</span><span class="n">vcpkg</span> <span class="n">integrate</span> <span class="n">install</span>
|
||||
|
@ -240,6 +287,17 @@ the previous section. Then compile the <code class="docutils literal notranslate
|
|||
contributors. If the version is out of date, please <a class="reference external" href="https://github.com/Microsoft/vcpkg">create an issue or pull
|
||||
request</a> on the vcpkg repository.</p>
|
||||
</div>
|
||||
<div class="section" id="lhelper">
|
||||
<h2>LHelper<a class="headerlink" href="#lhelper" title="Permalink to this headline">¶</a></h2>
|
||||
<p>You can download and install fmt using
|
||||
<a class="reference external" href="https://github.com/franko/lhelper">lhelper</a> dependency manager:</p>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">lhelper</span> <span class="n">activate</span> <span class="o"><</span><span class="n">some</span><span class="o">-</span><span class="n">environment</span><span class="o">></span>
|
||||
<span class="n">lhelper</span> <span class="n">install</span> <span class="n">fmt</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>All the recipes for lhelper are kept in the
|
||||
<a class="reference external" href="https://github.com/franko/lhelper-recipes">lhelper’s recipe</a> repository.</p>
|
||||
</div>
|
||||
<div class="section" id="android-ndk">
|
||||
<h2>Android NDK<a class="headerlink" href="#android-ndk" title="Permalink to this headline">¶</a></h2>
|
||||
<p>fmt provides <a class="reference external" href="https://github.com/fmtlib/fmt/blob/master/Android.mk">Android.mk file</a> that can be used to build the library
|
||||
|
@ -251,7 +309,7 @@ repository.</p>
|
|||
<div class="section" id="homebrew">
|
||||
<h2>Homebrew<a class="headerlink" href="#homebrew" title="Permalink to this headline">¶</a></h2>
|
||||
<p>fmt can be installed on OS X using <a class="reference external" href="http://brew.sh/">Homebrew</a>:</p>
|
||||
<div class="highlight-c++ notranslate"><div class="highlight"><pre><span></span><span class="n">brew</span> <span class="n">install</span> <span class="n">fmt</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span></span><span class="n">brew</span> <span class="n">install</span> <span class="n">fmt</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -266,7 +324,7 @@ repository.</p>
|
|||
|
||||
<div class="footer" role="contentinfo">
|
||||
© Copyright 2012-present, Victor Zverovich.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.8.5.
|
||||
Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.4.1+.
|
||||
</div>
|
||||
|
||||
<script src="_static/bootstrap.min.js"></script>
|
||||
|
|
|
@ -23,7 +23,7 @@ Format API
|
|||
|
||||
The format API is similar in spirit to the C ``printf`` family of function but
|
||||
is safer, simpler and several times `faster
|
||||
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
|
||||
<https://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
|
||||
than common standard library implementations.
|
||||
The `format string syntax <syntax.html>`_ is similar to the one used by
|
||||
`str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_ in
|
||||
|
@ -48,21 +48,19 @@ The ``fmt::print`` function performs formatting and writes the result to a strea
|
|||
|
||||
fmt::print(stderr, "System error code = {}\n", errno);
|
||||
|
||||
The file argument can be omitted in which case the function prints to
|
||||
``stdout``:
|
||||
If you omit the file argument the function will print to ``stdout``:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
fmt::print("Don't {}\n", "panic");
|
||||
|
||||
The Format API also supports positional arguments useful for localization:
|
||||
The format API also supports positional arguments useful for localization:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
fmt::print("I'd rather be {1} than {0}.", "right", "happy");
|
||||
|
||||
Named arguments can be created with ``fmt::arg``. This makes it easier to track
|
||||
what goes where when multiple arguments are being formatted:
|
||||
You can pass named arguments with ``fmt::arg``:
|
||||
|
||||
.. code:: c++
|
||||
|
||||
|
@ -91,16 +89,17 @@ time. For example, the code
|
|||
|
||||
fmt::format("The answer is {:d}", "forty-two");
|
||||
|
||||
throws a ``format_error`` exception with description "unknown format code 'd' for
|
||||
string", because the argument ``"forty-two"`` is a string while the format code
|
||||
``d`` only applies to integers, while
|
||||
throws the ``format_error`` exception because the argument ``"forty-two"`` is a
|
||||
string while the format code ``d`` only applies to integers.
|
||||
|
||||
The code
|
||||
|
||||
.. code:: c++
|
||||
|
||||
format(FMT_STRING("The answer is {:d}"), "forty-two");
|
||||
|
||||
reports a compile-time error for the same reason on compilers that support
|
||||
relaxed ``constexpr``. See `here <api.html#c.fmt>`_ for details.
|
||||
reports a compile-time error on compilers that support relaxed ``constexpr``.
|
||||
See `here <api.html#c.fmt>`_ for details.
|
||||
|
||||
The following code
|
||||
|
||||
|
@ -117,13 +116,13 @@ formatted into a narrow string. You can use a wide format string instead:
|
|||
|
||||
For comparison, writing a wide character to ``std::ostream`` results in
|
||||
its numeric value being written to the stream (i.e. 1070 instead of letter 'ю'
|
||||
which is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is
|
||||
needed.
|
||||
which is represented by ``L'\x42e'`` if we use Unicode) which is rarely
|
||||
desirable.
|
||||
|
||||
Compact Binary Code
|
||||
-------------------
|
||||
|
||||
The library is designed to produce compact per-call compiled code. For example
|
||||
The library produces compact per-call compiled code. For example
|
||||
(`godbolt <https://godbolt.org/g/TZU4KF>`_),
|
||||
|
||||
.. code:: c++
|
||||
|
@ -144,8 +143,8 @@ compiles to just
|
|||
mov rcx, rsp
|
||||
mov edi, offset .L.str
|
||||
mov esi, 17
|
||||
mov edx, 2
|
||||
call fmt::v5::vprint(fmt::v5::basic_string_view<char>, fmt::v5::format_args)
|
||||
mov edx, 1
|
||||
call fmt::v7::vprint(fmt::v7::basic_string_view<char>, fmt::v7::format_args)
|
||||
xor eax, eax
|
||||
add rsp, 24
|
||||
ret
|
||||
|
@ -167,20 +166,19 @@ The library is highly portable and relies only on a small set of C++11 features:
|
|||
* deleted functions
|
||||
* alias templates
|
||||
|
||||
These are available since GCC 4.8, Clang 3.0 and MSVC 19.0 (2015). For older
|
||||
compilers use {fmt} `version 4.x
|
||||
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
|
||||
maintained and only requires C++98.
|
||||
These are available in GCC 4.8, Clang 3.0, MSVC 19.0 (2015) and more recent
|
||||
compiler version. For older compilers use {fmt} `version 4.x
|
||||
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which is maintained and
|
||||
only requires C++98.
|
||||
|
||||
The output of all formatting functions is consistent across platforms. In
|
||||
particular, formatting a floating-point infinity always gives ``inf`` while the
|
||||
output of ``printf`` is platform-dependent. For example,
|
||||
The output of all formatting functions is consistent across platforms.
|
||||
For example,
|
||||
|
||||
.. code::
|
||||
|
||||
fmt::print("{}", std::numeric_limits<double>::infinity());
|
||||
|
||||
always prints ``inf``.
|
||||
always prints ``inf`` while the output of ``printf`` is platform-dependent.
|
||||
|
||||
.. _ease-of-use:
|
||||
|
||||
|
|
|
@ -52,6 +52,14 @@ To build a `shared library`__ set the ``BUILD_SHARED_LIBS`` CMake variable to
|
|||
|
||||
__ http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries
|
||||
|
||||
|
||||
To build a `static library` with position independent code (required if the main
|
||||
consumer of the fmt library is a shared library i.e. a Python extension) set the
|
||||
``CMAKE_POSITION_INDEPENDENT_CODE`` CMake variable to ``TRUE``::
|
||||
|
||||
cmake -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE ...
|
||||
|
||||
|
||||
Installing the Library
|
||||
======================
|
||||
|
||||
|
@ -83,6 +91,49 @@ Setting up your target to use a header-only version of ``fmt`` is equally easy::
|
|||
|
||||
target_link_libraries(<your-target> PRIVATE fmt::fmt-header-only)
|
||||
|
||||
Usage with build2
|
||||
=================
|
||||
|
||||
You can use `build2 <https://build2.org>`_, a dependency manager and a
|
||||
build-system combined, to use ``fmt``.
|
||||
|
||||
Currently this package is available in these package repositories:
|
||||
|
||||
- **https://cppget.org/fmt/** for released and published versions.
|
||||
- `The git repository with the sources of the build2 package of fmt <https://github.com/build2-packaging/fmt.git>`_
|
||||
for unreleased or custom revisions of ``fmt``.
|
||||
|
||||
**Usage:**
|
||||
|
||||
- ``build2`` package name: ``fmt``
|
||||
- Library target name : ``lib{fmt}``
|
||||
|
||||
For example, to make your ``build2`` project depend on ``fmt``:
|
||||
|
||||
- Add one of the repositories to your configurations, or in your
|
||||
``repositories.manifest``, if not already there::
|
||||
|
||||
:
|
||||
role: prerequisite
|
||||
location: https://pkg.cppget.org/1/stable
|
||||
|
||||
- Add this package as a dependency to your ``./manifest`` file
|
||||
(example for ``v7.0.x``)::
|
||||
|
||||
depends: fmt ~7.0.0
|
||||
|
||||
- Import the target and use it as a prerequisite to your own target
|
||||
using `fmt` in the appropriate ``buildfile``::
|
||||
|
||||
import fmt = fmt%lib{fmt}
|
||||
lib{mylib} : cxx{**} ... $fmt
|
||||
|
||||
Then build your project as usual with `b` or `bdep update`.
|
||||
|
||||
For ``build2`` newcomers or to get more details and use cases, you can read the
|
||||
``build2``
|
||||
`toolchain introduction <https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml>`_.
|
||||
|
||||
Building the Documentation
|
||||
==========================
|
||||
|
||||
|
@ -130,6 +181,18 @@ The fmt port in vcpkg is kept up to date by Microsoft team members and community
|
|||
contributors. If the version is out of date, please `create an issue or pull
|
||||
request <https://github.com/Microsoft/vcpkg>`__ on the vcpkg repository.
|
||||
|
||||
LHelper
|
||||
=======
|
||||
|
||||
You can download and install fmt using
|
||||
`lhelper <https://github.com/franko/lhelper>`__ dependency manager::
|
||||
|
||||
lhelper activate <some-environment>
|
||||
lhelper install fmt
|
||||
|
||||
All the recipes for lhelper are kept in the
|
||||
`lhelper's recipe <https://github.com/franko/lhelper-recipes>`__ repository.
|
||||
|
||||
Android NDK
|
||||
===========
|
||||
|
||||
|
|
|
@ -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(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!
|
||||
if (fmt::detail::is_negative(from)) {
|
||||
ec = 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
// From is positive. Can it always fit in To?
|
||||
if (F::digits <= T::digits) {
|
||||
// yes, From always fits in To.
|
||||
} else {
|
||||
// from may not fit in To, we have to do a dynamic check
|
||||
if (from > static_cast<From>((T::max)())) {
|
||||
ec = 1;
|
||||
return {};
|
||||
}
|
||||
if (F::digits > T::digits &&
|
||||
from > static_cast<From>(detail::max_value<To>())) {
|
||||
ec = 1;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (!F::is_signed && T::is_signed) {
|
||||
// can from be held in To?
|
||||
if (F::digits < T::digits) {
|
||||
// yes, From always fits in To.
|
||||
} else {
|
||||
// from may not fit in To, we have to do a dynamic check
|
||||
if (from > static_cast<From>((T::max)())) {
|
||||
// outside range.
|
||||
ec = 1;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
|
||||
from > static_cast<From>(detail::max_value<To>())) {
|
||||
ec = 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
// reaching here means all is ok for lossless conversion.
|
||||
return static_cast<To>(from);
|
||||
|
||||
} // function
|
||||
return static_cast<To>(from); // Lossless conversion.
|
||||
}
|
||||
|
||||
template <typename To, typename From,
|
||||
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
|
||||
IntermediateRep count =
|
||||
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
|
||||
if (ec) {
|
||||
return {};
|
||||
}
|
||||
if (ec) return {};
|
||||
// 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;
|
||||
if (count > max1) {
|
||||
ec = 1;
|
||||
|
@ -209,17 +191,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|||
count *= Factor::num;
|
||||
}
|
||||
|
||||
// this can't go wrong, right? den>0 is checked earlier.
|
||||
if (Factor::den != 1) {
|
||||
count /= Factor::den;
|
||||
}
|
||||
// convert to the to type, safely
|
||||
using ToRep = typename To::rep;
|
||||
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
|
||||
if (ec) {
|
||||
return {};
|
||||
}
|
||||
return To{tocount};
|
||||
if (detail::const_check(Factor::den != 1)) count /= Factor::den;
|
||||
auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
|
||||
return ec ? To() : To(tocount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -351,6 +325,11 @@ inline std::tm localtime(std::time_t time) {
|
|||
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
|
||||
inline std::tm gmtime(std::time_t time) {
|
||||
struct dispatcher {
|
||||
|
@ -387,6 +366,11 @@ inline std::tm gmtime(std::time_t time) {
|
|||
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 {
|
||||
inline size_t strftime(char* str, size_t count, const char* format,
|
||||
const std::tm* time) {
|
||||
|
@ -399,6 +383,17 @@ inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
|
|||
}
|
||||
} // 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 ParseContext>
|
||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
|
|
|
@ -463,16 +463,16 @@ template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
|||
}
|
||||
|
||||
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* end = begin + sizeof(data::reset_color) - 1;
|
||||
buffer.append(begin, end);
|
||||
}
|
||||
|
||||
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_format_args<buffer_context<Char>> args) {
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
bool has_style = false;
|
||||
if (ts.has_emphasis()) {
|
||||
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>>
|
||||
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;
|
||||
detail::vformat_to(buf, ts, to_string_view(format), args);
|
||||
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
|
||||
escape sequences to specify text formatting.
|
||||
Example:
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
detail::check_format_string<Args...>(format_str);
|
||||
using context = buffer_context<char_t<S>>;
|
||||
format_arg_store<context, Args...> as{args...};
|
||||
vprint(f, ts, format_str, basic_format_args<context>(as));
|
||||
vprint(f, ts, format_str,
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
const Args&... args) {
|
||||
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
|
||||
|
|
|
@ -368,7 +368,8 @@ template <typename... Args> struct type_list {};
|
|||
|
||||
// Returns a reference to the argument at index N from [first, rest...].
|
||||
template <int N, typename T, typename... Args>
|
||||
constexpr const auto& get(const T& first, const Args&... rest) {
|
||||
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");
|
||||
if constexpr (N == 0)
|
||||
return first;
|
||||
|
@ -406,6 +407,19 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
|||
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.
|
||||
template <typename Char, typename T, int N> struct field {
|
||||
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 {
|
||||
// This ensures that the argument type is convertile to `const T&`.
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
@ -489,16 +505,17 @@ constexpr auto parse_tail(T head, S format_str) {
|
|||
template <typename T, typename Char> struct parse_specs_result {
|
||||
formatter<T, Char> fmt;
|
||||
size_t end;
|
||||
int next_arg_id;
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
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);
|
||||
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 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
|
||||
|
@ -518,8 +535,8 @@ constexpr auto compile_format_string(S format_str) {
|
|||
format_str);
|
||||
} else if constexpr (str[POS + 1] == ':') {
|
||||
using type = get_type<ID, Args>;
|
||||
constexpr auto result = parse_specs<type>(str, POS + 2);
|
||||
return parse_tail<Args, result.end, ID + 1>(
|
||||
constexpr auto result = parse_specs<type>(str, POS + 2, ID);
|
||||
return parse_tail<Args, result.end, result.next_arg_id>(
|
||||
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
||||
} else {
|
||||
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);
|
||||
} else {
|
||||
constexpr auto end = parse_text(str, POS + 1);
|
||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
||||
format_str);
|
||||
if constexpr (end - POS > 1) {
|
||||
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,
|
||||
const Args&... args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
detail::buffer<Char>& base = buffer;
|
||||
cf.format(std::back_inserter(base), args...);
|
||||
cf.format(detail::buffer_appender<Char>(buffer), args...);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
|
@ -608,8 +629,7 @@ template <typename CompiledFormat, typename... Args,
|
|||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
using context = buffer_context<Char>;
|
||||
detail::buffer<Char>& base = buffer;
|
||||
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
|
||||
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
|
||||
make_format_args<context>(args...));
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
@ -618,9 +638,13 @@ template <typename S, typename... Args,
|
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||
Args&&... 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...));
|
||||
#ifdef __cpp_if_constexpr
|
||||
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||
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());
|
||||
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...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
|
||||
detail::basic_compiled_format, CompiledFormat>::value)>
|
||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||
const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args>
|
||||
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
|
||||
const Args&... args) ->
|
||||
typename std::enable_if<
|
||||
detail::is_output_iterator<OutputIt,
|
||||
typename CompiledFormat::char_type>::value &&
|
||||
std::is_base_of<detail::basic_compiled_format,
|
||||
CompiledFormat>::value,
|
||||
format_to_n_result<OutputIt>>::type {
|
||||
auto it =
|
||||
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
|
||||
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>
|
||||
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||
return format_to(detail::counting_iterator(), cf, args...).count();
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include <vector>
|
||||
|
||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||
#define FMT_VERSION 70003
|
||||
#define FMT_VERSION 70103
|
||||
|
||||
#ifdef __clang__
|
||||
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
||||
|
@ -57,6 +57,7 @@
|
|||
# define FMT_MSC_VER 0
|
||||
# define FMT_SUPPRESS_MSC_WARNING(n)
|
||||
#endif
|
||||
|
||||
#ifdef __has_feature
|
||||
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
||||
#else
|
||||
|
@ -64,7 +65,7 @@
|
|||
#endif
|
||||
|
||||
#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)
|
||||
#else
|
||||
# define FMT_HAS_INCLUDE(x) 0
|
||||
|
@ -99,7 +100,7 @@
|
|||
#endif
|
||||
|
||||
#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
|
||||
# define FMT_OVERRIDE override
|
||||
# else
|
||||
|
@ -152,7 +153,7 @@
|
|||
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
|
||||
# define FMT_DEPRECATED [[deprecated]]
|
||||
# else
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
|
||||
# define FMT_DEPRECATED __attribute__((deprecated))
|
||||
# elif FMT_MSC_VER
|
||||
# define FMT_DEPRECATED __declspec(deprecated)
|
||||
|
@ -177,9 +178,17 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FMT_BEGIN_NAMESPACE
|
||||
#ifndef FMT_USE_INLINE_NAMESPACES
|
||||
# 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_END_NAMESPACE \
|
||||
} \
|
||||
|
@ -269,8 +278,7 @@ struct monostate {};
|
|||
|
||||
namespace detail {
|
||||
|
||||
// A helper function to suppress bogus "conditional expression is constant"
|
||||
// warnings.
|
||||
// A helper function to suppress "conditional expression is constant" warnings.
|
||||
template <typename T> constexpr T const_check(T value) { return value; }
|
||||
|
||||
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
|
||||
// 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
|
||||
using int128_t = __int128_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;
|
||||
};
|
||||
|
||||
// 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 {
|
||||
constexpr 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;
|
||||
|
||||
explicit constexpr basic_format_parse_context(
|
||||
basic_string_view<Char> format_str, ErrorHandler eh = {})
|
||||
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
|
||||
basic_string_view<Char> format_str, ErrorHandler eh = {},
|
||||
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
|
||||
|
@ -616,8 +638,24 @@ template <typename T, typename Context>
|
|||
using has_formatter =
|
||||
std::is_constructible<typename Context::template formatter_type<T>>;
|
||||
|
||||
// Checks whether T is a container with contiguous storage.
|
||||
template <typename T> struct is_contiguous : std::false_type {};
|
||||
template <typename Char>
|
||||
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
|
||||
|
||||
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
|
||||
A contiguous memory buffer with an optional growing ability. It is an internal
|
||||
|
@ -640,6 +678,8 @@ template <typename T> class buffer {
|
|||
size_(sz),
|
||||
capacity_(cap) {}
|
||||
|
||||
~buffer() = default;
|
||||
|
||||
/** Sets the buffer data and capacity. */
|
||||
void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
|
||||
ptr_ = buf_data;
|
||||
|
@ -655,7 +695,6 @@ template <typename T> class buffer {
|
|||
|
||||
buffer(const buffer&) = delete;
|
||||
void operator=(const buffer&) = delete;
|
||||
virtual ~buffer() = default;
|
||||
|
||||
T* begin() FMT_NOEXCEPT { return ptr_; }
|
||||
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
|
||||
|
@ -675,24 +714,26 @@ template <typename T> class buffer {
|
|||
/** Returns a pointer to the buffer data. */
|
||||
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. */
|
||||
void clear() { size_ = 0; }
|
||||
|
||||
/** Reserves space to store at least *capacity* elements. */
|
||||
void reserve(size_t new_capacity) {
|
||||
// Tries resizing the buffer to contain *count* elements. If T is a POD type
|
||||
// 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);
|
||||
}
|
||||
|
||||
void push_back(const T& value) {
|
||||
reserve(size_ + 1);
|
||||
try_reserve(size_ + 1);
|
||||
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>
|
||||
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:
|
||||
Container& container_;
|
||||
|
||||
protected:
|
||||
void grow(size_t capacity) FMT_OVERRIDE {
|
||||
void grow(size_t capacity) final FMT_OVERRIDE {
|
||||
container_.resize(capacity);
|
||||
this->set(&container_[0], capacity);
|
||||
}
|
||||
|
||||
public:
|
||||
explicit container_buffer(Container& c)
|
||||
explicit iterator_buffer(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.
|
||||
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;
|
||||
// A buffer that counts the number of code units written discarding the output.
|
||||
template <typename T = char> class counting_buffer final : public buffer<T> {
|
||||
private:
|
||||
enum { buffer_size = 256 };
|
||||
T data_[buffer_size];
|
||||
size_t count_ = 0;
|
||||
|
||||
protected:
|
||||
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>
|
||||
|
@ -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>
|
||||
struct arg_data {
|
||||
// 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];
|
||||
|
||||
template <typename... U>
|
||||
|
@ -771,7 +931,8 @@ struct arg_data {
|
|||
|
||||
template <typename T, typename Char, size_t NUM_ARGS>
|
||||
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>
|
||||
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 ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
|
||||
|
||||
struct unformattable {};
|
||||
|
||||
// Maps formatting arguments to core types.
|
||||
template <typename Context> struct arg_mapper {
|
||||
using char_type = typename Context::char_type;
|
||||
|
@ -1067,15 +1230,7 @@ template <typename Context> struct arg_mapper {
|
|||
return map(val.value);
|
||||
}
|
||||
|
||||
int map(...) {
|
||||
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;
|
||||
}
|
||||
unformattable map(...) { return {}; }
|
||||
};
|
||||
|
||||
// A type constant after applying arg_mapper<Context>.
|
||||
|
@ -1199,15 +1354,25 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg(
|
|||
return vis(monostate());
|
||||
}
|
||||
|
||||
// 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 {};
|
||||
template <typename Char>
|
||||
struct is_contiguous<detail::buffer<Char>> : std::true_type {};
|
||||
template <typename T> struct formattable : std::false_type {};
|
||||
|
||||
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>
|
||||
struct is_back_insert_iterator : std::false_type {};
|
||||
template <typename Container>
|
||||
|
@ -1219,6 +1384,9 @@ struct is_contiguous_back_insert_iterator : std::false_type {};
|
|||
template <typename Container>
|
||||
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
|
||||
: is_contiguous<Container> {};
|
||||
template <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.
|
||||
class locale_ref {
|
||||
|
@ -1250,13 +1418,24 @@ FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) {
|
|||
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
|
||||
// a fallback formatter in one translation unit and an implicit conversion in
|
||||
// another (not recommended).
|
||||
template <bool IS_PACKED, typename Context, type, typename T,
|
||||
FMT_ENABLE_IF(IS_PACKED)>
|
||||
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,
|
||||
|
@ -1356,13 +1535,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
|
|||
|
||||
template <typename Char>
|
||||
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 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) \
|
||||
basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>
|
||||
basic_format_context<detail::buffer_appender<Char>, Char>
|
||||
|
||||
/**
|
||||
\rst
|
||||
|
@ -1414,7 +1593,7 @@ class format_arg_store
|
|||
|
||||
/**
|
||||
\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`
|
||||
can be omitted in which case it defaults to `~fmt::context`.
|
||||
See `~fmt::arg` for lifetime considerations.
|
||||
|
@ -1426,6 +1605,27 @@ inline format_arg_store<Context, Args...> make_format_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
|
||||
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.
|
||||
struct format_args : basic_format_args<format_context> {
|
||||
template <typename... Args>
|
||||
|
@ -1738,32 +1945,10 @@ struct format_args : basic_format_args<format_context> {
|
|||
struct wformat_args : basic_format_args<wformat_context> {
|
||||
using basic_format_args::basic_format_args;
|
||||
};
|
||||
#endif
|
||||
|
||||
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)>
|
||||
std::basic_string<Char> vformat(
|
||||
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);
|
||||
|
||||
template <typename Char>
|
||||
typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
|
||||
void vformat_to(
|
||||
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,
|
||||
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``. */
|
||||
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
|
||||
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
|
||||
template <
|
||||
typename OutputIt, typename S, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
|
||||
OutputIt vformat_to(
|
||||
OutputIt out, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
auto& c = detail::get_container(out);
|
||||
detail::container_buffer<remove_reference_t<decltype(c)>> buf(c);
|
||||
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
||||
bool enable = detail::is_output_iterator<OutputIt, Char>::value>
|
||||
auto vformat_to(OutputIt out, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> typename std::enable_if<enable, OutputIt>::type {
|
||||
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||
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(
|
||||
is_contiguous<Container>::value&& detail::is_string<S>::value)>
|
||||
inline std::back_insert_iterator<Container> format_to(
|
||||
std::back_insert_iterator<Container> out, const S& format_str,
|
||||
Args&&... args) {
|
||||
return vformat_to(out, to_string_view(format_str),
|
||||
detail::make_args_checked<Args...>(format_str, args...));
|
||||
/**
|
||||
\rst
|
||||
Formats arguments, 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), "{}", 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>>
|
||||
|
@ -1832,7 +2072,7 @@ FMT_INLINE std::basic_string<Char> vformat(
|
|||
// std::basic_string<char_t<S>> to reduce the symbol size.
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1852,7 +2092,7 @@ FMT_API void vprint(std::FILE*, string_view, format_args);
|
|||
*/
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
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>()
|
||||
? vprint(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>>
|
||||
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>()
|
||||
? vprint(to_string_view(format_str), vargs)
|
||||
: detail::vprint_mojibake(stdout, to_string_view(format_str),
|
||||
|
|
|
@ -15,22 +15,12 @@
|
|||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
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>
|
||||
std::basic_string<Char> vformat(
|
||||
const std::locale& loc, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
detail::vformat_to(loc, buffer, format_str, args);
|
||||
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
|
||||
return fmt::to_string(buffer);
|
||||
}
|
||||
} // namespace detail
|
||||
|
@ -45,32 +35,28 @@ inline std::basic_string<Char> vformat(
|
|||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const std::locale& loc,
|
||||
const S& format_str, Args&&... args) {
|
||||
return detail::vformat(
|
||||
loc, to_string_view(format_str),
|
||||
detail::make_args_checked<Args...>(format_str, args...));
|
||||
return detail::vformat(loc, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
template <typename S, typename OutputIt, typename... Args,
|
||||
typename Char = enable_if_t<
|
||||
detail::is_output_iterator<OutputIt>::value, char_t<S>>>
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||
inline OutputIt vformat_to(
|
||||
OutputIt out, const std::locale& loc, const S& format_str,
|
||||
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
||||
using af = detail::arg_formatter<OutputIt, Char>;
|
||||
return vformat_to<af>(out, to_string_view(format_str), args,
|
||||
detail::locale_ref(loc));
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
|
||||
detail::is_string<S>::value)>
|
||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
||||
const S& format_str, Args&&... args) {
|
||||
detail::check_format_string<Args...>(format_str);
|
||||
using context = format_context_t<OutputIt, char_t<S>>;
|
||||
format_arg_store<context, Args...> as{args...};
|
||||
return vformat_to(out, loc, to_string_view(format_str),
|
||||
basic_format_args<context>(as));
|
||||
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
|
||||
inline auto format_to(OutputIt out, const std::locale& loc,
|
||||
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, loc, to_string_view(format_str), vargs);
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
#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))
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
|
@ -278,7 +279,8 @@ class file {
|
|||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
||||
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.
|
||||
|
@ -343,36 +345,69 @@ class file {
|
|||
// Returns the memory page size.
|
||||
long getpagesize();
|
||||
|
||||
class direct_buffered_file;
|
||||
namespace detail {
|
||||
|
||||
template <typename S, typename... Args>
|
||||
void print(direct_buffered_file& f, const S& format_str,
|
||||
const Args&... args);
|
||||
struct buffer_size {
|
||||
size_t value = 0;
|
||||
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.
|
||||
class direct_buffered_file {
|
||||
struct ostream_params {
|
||||
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:
|
||||
file file_;
|
||||
|
||||
enum { buffer_size = 4096 };
|
||||
char buffer_[buffer_size];
|
||||
int pos_;
|
||||
|
||||
void flush() {
|
||||
if (pos_ == 0) return;
|
||||
file_.write(buffer_, pos_);
|
||||
pos_ = 0;
|
||||
if (size() == 0) return;
|
||||
file_.write(data(), size());
|
||||
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:
|
||||
direct_buffered_file(cstring_view path, int oflag)
|
||||
: file_(path, oflag), pos_(0) {}
|
||||
|
||||
~direct_buffered_file() {
|
||||
flush();
|
||||
ostream(ostream&& other)
|
||||
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
||||
file_(std::move(other.file_)) {
|
||||
other.set(nullptr, 0);
|
||||
}
|
||||
~ostream() {
|
||||
flush();
|
||||
delete[] data();
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
friend ostream output_file(cstring_view path, T... params);
|
||||
|
||||
void close() {
|
||||
flush();
|
||||
|
@ -380,25 +415,20 @@ class direct_buffered_file {
|
|||
}
|
||||
|
||||
template <typename S, typename... Args>
|
||||
friend void print(direct_buffered_file& f, const S& format_str,
|
||||
const Args&... 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);
|
||||
void print(const S& format_str, const Args&... args) {
|
||||
format_to(detail::buffer_appender<char>(*this), format_str, args...);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
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
|
||||
|
||||
#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> {
|
||||
private:
|
||||
// Hide all operator<< from std::basic_ostream<Char>.
|
||||
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);
|
||||
void_t<> operator<<(converter);
|
||||
};
|
||||
|
||||
// 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
|
||||
// std::ostream).
|
||||
template <typename T, typename Char> class is_streamable {
|
||||
|
@ -103,7 +113,7 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||
#endif
|
||||
output << value;
|
||||
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<<.
|
||||
|
@ -160,7 +170,7 @@ template <typename S, typename... Args,
|
|||
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) {
|
||||
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
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ template <typename Char> class printf_width_handler {
|
|||
template <typename Char, typename Context>
|
||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
Context(std::back_inserter(buf), format, args).format();
|
||||
Context(buffer_appender<Char>(buf), format, args).format();
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
|
@ -598,7 +598,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||
|
||||
template <typename Char>
|
||||
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 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));
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
|
||||
|
||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
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) {
|
||||
return add_space ? L" '{}'" : L"'{}'";
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
template <typename RangeT, typename Char>
|
||||
struct formatter<RangeT, Char,
|
||||
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
||||
template <typename T, typename Char>
|
||||
struct formatter<
|
||||
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;
|
||||
|
||||
template <typename ParseContext>
|
||||
|
@ -257,8 +268,7 @@ struct formatter<RangeT, Char,
|
|||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(const RangeT& values,
|
||||
FormatContext& ctx) {
|
||||
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
|
||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
||||
size_t i = 0;
|
||||
auto it = values.begin();
|
||||
|
|
6
fmt/pull_request_template.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
<!-- Please read the contribution guidelines before submitting a pull request. -->
|
||||
<!-- By submitting this pull request, you agree that your contributions are licensed under the {fmt} license,
|
||||
and agree to future changes to the licensing. -->
|
||||
<!-- If you're a first-time contributor, please acknowledge it by leaving the statement below. -->
|
||||
|
||||
I agree that my contributions are licensed under the {fmt} license, and agree to future changes to the licensing.
|
|
@ -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)
|
||||
: 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
|
||||
|
||||
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 FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
|
||||
template FMT_API void detail::vformat_to(
|
||||
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,
|
||||
detail::buffer<char>&);
|
||||
|
|
|
@ -62,7 +62,7 @@ using RWResult = int;
|
|||
inline unsigned convert_rwcount(std::size_t count) {
|
||||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||
}
|
||||
#else
|
||||
#elif FMT_USE_FCNTL
|
||||
// Return type of read and write functions.
|
||||
using RWResult = ssize_t;
|
||||
|
||||
|
@ -124,7 +124,8 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
|||
if (result != 0) {
|
||||
utf16_to_utf8 utf8_message;
|
||||
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;
|
||||
}
|
||||
break;
|
||||
|
@ -288,12 +289,12 @@ void file::pipe(file& read_end, file& write_end) {
|
|||
}
|
||||
|
||||
buffered_file file::fdopen(const char* mode) {
|
||||
// Don't retry as fdopen doesn't return EINTR.
|
||||
#if defined(__MINGW32__) && defined(_POSIX_)
|
||||
// Don't retry as fdopen doesn't return EINTR.
|
||||
# if defined(__MINGW32__) && defined(_POSIX_)
|
||||
FILE* f = ::fdopen(fd_, mode);
|
||||
#else
|
||||
# else
|
||||
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||
#endif
|
||||
# endif
|
||||
if (!f)
|
||||
FMT_THROW(
|
||||
system_error(errno, "cannot associate stream with file descriptor"));
|
||||
|
@ -313,5 +314,9 @@ long getpagesize() {
|
|||
return size;
|
||||
# endif
|
||||
}
|
||||
|
||||
FMT_API void ostream::grow(size_t) {
|
||||
if (this->size() == this->capacity()) flush();
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
FMT_END_NAMESPACE
|
||||
|
|
|
@ -2,5 +2,3 @@ This directory contains build support files such as
|
|||
|
||||
* CMake modules
|
||||
* Build scripts
|
||||
* qmake (static build with dynamic libc only)
|
||||
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
# Staticlib configuration for qmake builds
|
||||
# For some reason qmake 3.1 fails to identify source dependencies and excludes format.cc and printf.cc
|
||||
# from compilation so it _MUST_ be called as qmake -nodepend
|
||||
# A workaround is implemented below: a custom compiler is defined which does not track dependencies
|
||||
|
||||
TEMPLATE = lib
|
||||
|
||||
TARGET = fmt
|
||||
|
||||
QMAKE_EXT_CPP = .cc
|
||||
|
||||
CONFIG = staticlib warn_on c++11
|
||||
|
||||
FMT_SOURCES = \
|
||||
../src/format.cc \
|
||||
../src/posix.cc
|
||||
|
||||
fmt.name = libfmt
|
||||
fmt.input = FMT_SOURCES
|
||||
fmt.output = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ
|
||||
fmt.clean = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ
|
||||
fmt.depends = ${QMAKE_FILE_IN}
|
||||
# QMAKE_RUN_CXX will not be expanded
|
||||
fmt.commands = $$QMAKE_CXX -c $$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_WARN_ON $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO $$QMAKE_CXXFLAGS_CXX11 ${QMAKE_FILE_IN}
|
||||
fmt.variable_out = OBJECTS
|
||||
fmt.CONFIG = no_dependencies no_link
|
||||
QMAKE_EXTRA_COMPILERS += fmt
|
|
@ -1,30 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# Update the coverity branch from the master branch.
|
||||
# It is not done automatically because Coverity Scan limits
|
||||
# the number of submissions per day.
|
||||
|
||||
from __future__ import print_function
|
||||
import shutil, tempfile
|
||||
from subprocess import check_output, STDOUT
|
||||
|
||||
class Git:
|
||||
def __init__(self, dir):
|
||||
self.dir = dir
|
||||
|
||||
def __call__(self, *args):
|
||||
output = check_output(['git'] + list(args), cwd=self.dir, stderr=STDOUT)
|
||||
print(output)
|
||||
return output
|
||||
|
||||
dir = tempfile.mkdtemp()
|
||||
try:
|
||||
git = Git(dir)
|
||||
git('clone', '-b', 'coverity', 'git@github.com:fmtlib/fmt.git', dir)
|
||||
output = git('merge', '-X', 'theirs', '--no-commit', 'origin/master')
|
||||
if 'Fast-forward' not in output:
|
||||
git('reset', 'HEAD', '.travis.yml')
|
||||
git('checkout', '--', '.travis.yml')
|
||||
git('commit', '-m', 'Update coverity branch')
|
||||
git('push')
|
||||
finally:
|
||||
shutil.rmtree(dir)
|
|
@ -91,8 +91,6 @@ add_fmt_test(assert-test)
|
|||
add_fmt_test(chrono-test)
|
||||
add_fmt_test(color-test)
|
||||
add_fmt_test(core-test)
|
||||
add_fmt_test(grisu-test)
|
||||
target_compile_definitions(grisu-test PRIVATE FMT_USE_GRISU=1)
|
||||
add_fmt_test(gtest-extra-test)
|
||||
add_fmt_test(format-test mock-allocator.h)
|
||||
if (MSVC)
|
||||
|
@ -105,11 +103,21 @@ add_fmt_test(locale-test)
|
|||
add_fmt_test(ostream-test)
|
||||
add_fmt_test(compile-test)
|
||||
add_fmt_test(printf-test)
|
||||
add_fmt_test(custom-formatter-test)
|
||||
add_fmt_test(ranges-test)
|
||||
add_fmt_test(scan-test)
|
||||
|
||||
if (NOT MSVC_BUILD_STATIC)
|
||||
if (NOT DEFINED MSVC_STATIC_RUNTIME AND MSVC)
|
||||
foreach (flag_var
|
||||
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
if (${flag_var} MATCHES "^(/|-)(MT|MTd)")
|
||||
set(MSVC_STATIC_RUNTIME ON)
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if (NOT MSVC_STATIC_RUNTIME)
|
||||
add_fmt_executable(posix-mock-test
|
||||
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
|
||||
target_include_directories(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.1.0)
|
||||
cmake_minimum_required(VERSION 3.1...3.18)
|
||||
|
||||
project(fmt-test)
|
||||
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
// Formatting library for C++ - assertion tests
|
||||
// Formatting library for C++ - FMT_ASSERT test
|
||||
//
|
||||
// It is a separate test to minimize the number of EXPECT_DEBUG_DEATH checks
|
||||
// which are slow on some platforms. In other tests FMT_ASSERT is made to throw
|
||||
// an exception which is much faster and easier to check.
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
|
|
|
@ -95,6 +95,17 @@ TEST(TimeTest, GMTime) {
|
|||
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
|
||||
}
|
||||
|
||||
TEST(TimeTest, TimePoint) {
|
||||
std::chrono::system_clock::time_point point = std::chrono::system_clock::now();
|
||||
|
||||
std::time_t t = std::chrono::system_clock::to_time_t(point);
|
||||
std::tm tm = *std::localtime(&t);
|
||||
char strftime_output[256];
|
||||
std::strftime(strftime_output, sizeof(strftime_output), "It is %Y-%m-%d %H:%M:%S", &tm);
|
||||
|
||||
EXPECT_EQ(strftime_output, fmt::format("It is {:%Y-%m-%d %H:%M:%S}", point));
|
||||
}
|
||||
|
||||
#define EXPECT_TIME(spec, time, duration) \
|
||||
{ \
|
||||
std::locale loc("ja_JP.utf8"); \
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
#include "fmt/color.h"
|
||||
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "gtest-extra.h"
|
||||
|
||||
TEST(ColorsTest, ColorsPrint) {
|
||||
|
@ -84,3 +88,12 @@ TEST(ColorsTest, Format) {
|
|||
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
|
||||
"\x1b[31mfoo\x1b[0m");
|
||||
}
|
||||
|
||||
TEST(ColorsTest, FormatToOutAcceptsTextStyle) {
|
||||
fmt::text_style ts = fg(fmt::rgb(255, 20, 30));
|
||||
std::string out;
|
||||
fmt::format_to(std::back_inserter(out), ts, "rgb(255,20,30){}{}{}", 1, 2, 3);
|
||||
|
||||
EXPECT_EQ(fmt::to_string(out),
|
||||
"\x1b[38;2;255;020;030mrgb(255,20,30)123\x1b[0m");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Test if compile errors are produced where necessary.
|
||||
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
cmake_minimum_required(VERSION 3.1...3.18)
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CheckCXXCompilerFlag)
|
||||
|
|
|
@ -5,19 +5,10 @@
|
|||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <cctype>
|
||||
#include <cfloat>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
// Check if fmt/compile.h compiles with windows.h included before it.
|
||||
// Check that fmt/compile.h compiles with windows.h included before it.
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
@ -25,16 +16,8 @@
|
|||
#include "fmt/compile.h"
|
||||
#include "gmock.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "mock-allocator.h"
|
||||
#include "util.h"
|
||||
|
||||
#undef ERROR
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
using testing::Return;
|
||||
using testing::StrictMock;
|
||||
|
||||
// compiletime_prepared_parts_type_provider is useful only with relaxed
|
||||
// constexpr.
|
||||
#if FMT_USE_CONSTEXPR
|
||||
|
@ -114,20 +97,20 @@ TEST(CompileTest, MultipleTypes) {
|
|||
EXPECT_EQ(fmt::format(f, 42, 42), "42 42");
|
||||
}
|
||||
|
||||
struct formattable {};
|
||||
struct test_formattable {};
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <> struct formatter<formattable> : formatter<const char*> {
|
||||
template <> struct formatter<test_formattable> : formatter<const char*> {
|
||||
template <typename FormatContext>
|
||||
auto format(formattable, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto format(test_formattable, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
return formatter<const char*>::format("foo", ctx);
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(CompileTest, FormatUserDefinedType) {
|
||||
auto f = fmt::detail::compile<formattable>("{}");
|
||||
EXPECT_EQ(fmt::format(f, formattable()), "foo");
|
||||
auto f = fmt::detail::compile<test_formattable>("{}");
|
||||
EXPECT_EQ(fmt::format(f, test_formattable()), "foo");
|
||||
}
|
||||
|
||||
TEST(CompileTest, EmptyFormatString) {
|
||||
|
@ -146,21 +129,45 @@ TEST(CompileTest, FormatDefault) {
|
|||
EXPECT_EQ("4.2", fmt::format(FMT_COMPILE("{}"), 4.2));
|
||||
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), "foo"));
|
||||
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), std::string("foo")));
|
||||
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), formattable()));
|
||||
EXPECT_EQ("foo", fmt::format(FMT_COMPILE("{}"), test_formattable()));
|
||||
}
|
||||
|
||||
TEST(CompileTest, FormatWideString) {
|
||||
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{}"), 42));
|
||||
}
|
||||
|
||||
TEST(CompileTest, FormatSpecs) {
|
||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
|
||||
}
|
||||
|
||||
TEST(CompileTest, DynamicWidth) {
|
||||
EXPECT_EQ(" 42foo ",
|
||||
fmt::format(FMT_COMPILE("{:{}}{:{}}"), 42, 4, "foo", 5));
|
||||
}
|
||||
|
||||
TEST(CompileTest, FormatTo) {
|
||||
char buf[8];
|
||||
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);
|
||||
*end = '\0';
|
||||
EXPECT_STREQ("42", buf);
|
||||
end = fmt::format_to(buf, FMT_COMPILE("{:x}"), 42);
|
||||
*end = '\0';
|
||||
EXPECT_STREQ("2a", buf);
|
||||
}
|
||||
|
||||
TEST(CompileTest, FormatToNWithCompileMacro) {
|
||||
constexpr auto buffer_size = 8;
|
||||
char buffer[buffer_size];
|
||||
auto res = fmt::format_to_n(buffer, buffer_size, FMT_COMPILE("{}"), 42);
|
||||
*res.out = '\0';
|
||||
EXPECT_STREQ("42", buffer);
|
||||
res = fmt::format_to_n(buffer, buffer_size, FMT_COMPILE("{:x}"), 42);
|
||||
*res.out = '\0';
|
||||
EXPECT_STREQ("2a", buffer);
|
||||
}
|
||||
|
||||
TEST(CompileTest, TextAndArg) {
|
||||
EXPECT_EQ(">>>42<<<", fmt::format(FMT_COMPILE(">>>{}<<<"), 42));
|
||||
EXPECT_EQ("42!", fmt::format(FMT_COMPILE("{}!"), 42));
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -31,21 +31,16 @@
|
|||
using fmt::basic_format_arg;
|
||||
using fmt::string_view;
|
||||
using fmt::detail::buffer;
|
||||
using fmt::detail::make_arg;
|
||||
using fmt::detail::value;
|
||||
|
||||
using testing::_;
|
||||
using testing::Invoke;
|
||||
using testing::Return;
|
||||
using testing::StrictMock;
|
||||
|
||||
namespace {
|
||||
|
||||
struct test_struct {};
|
||||
|
||||
template <typename Context, typename T>
|
||||
basic_format_arg<Context> make_arg(const T& value) {
|
||||
return fmt::detail::make_arg<Context>(value);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <typename Char> struct formatter<test_struct, Char> {
|
||||
template <typename ParseContext>
|
||||
|
@ -53,10 +48,7 @@ template <typename Char> struct formatter<test_struct, Char> {
|
|||
return ctx.begin();
|
||||
}
|
||||
|
||||
typedef std::back_insert_iterator<buffer<Char>> iterator;
|
||||
|
||||
auto format(test_struct, basic_format_context<iterator, char>& ctx)
|
||||
-> decltype(ctx.out()) {
|
||||
auto format(test_struct, format_context& ctx) -> decltype(ctx.out()) {
|
||||
const Char* test = "test";
|
||||
return std::copy_n(test, std::strlen(test), ctx.out());
|
||||
}
|
||||
|
@ -81,22 +73,22 @@ TEST(BufferTest, Nonmoveable) {
|
|||
}
|
||||
#endif
|
||||
|
||||
// A test buffer with a dummy grow method.
|
||||
template <typename T> struct test_buffer : buffer<T> {
|
||||
void grow(size_t capacity) { this->set(nullptr, capacity); }
|
||||
};
|
||||
TEST(BufferTest, Indestructible) {
|
||||
static_assert(!std::is_destructible<fmt::detail::buffer<int>>(),
|
||||
"buffer's destructor is protected");
|
||||
}
|
||||
|
||||
template <typename T> struct mock_buffer : buffer<T> {
|
||||
MOCK_METHOD1(do_grow, void(size_t capacity));
|
||||
template <typename T> struct mock_buffer final : buffer<T> {
|
||||
MOCK_METHOD1(do_grow, size_t(size_t capacity));
|
||||
|
||||
void grow(size_t capacity) {
|
||||
this->set(this->data(), capacity);
|
||||
do_grow(capacity);
|
||||
void grow(size_t capacity) { this->set(this->data(), do_grow(capacity)); }
|
||||
|
||||
mock_buffer(T* data = nullptr, size_t capacity = 0) {
|
||||
this->set(data, capacity);
|
||||
ON_CALL(*this, do_grow(_)).WillByDefault(Invoke([](size_t capacity) {
|
||||
return capacity;
|
||||
}));
|
||||
}
|
||||
|
||||
mock_buffer() {}
|
||||
mock_buffer(T* data) { this->set(data, 0); }
|
||||
mock_buffer(T* data, size_t capacity) { this->set(data, capacity); }
|
||||
};
|
||||
|
||||
TEST(BufferTest, Ctor) {
|
||||
|
@ -123,24 +115,6 @@ TEST(BufferTest, Ctor) {
|
|||
}
|
||||
}
|
||||
|
||||
struct dying_buffer : test_buffer<int> {
|
||||
MOCK_METHOD0(die, void());
|
||||
~dying_buffer() { die(); }
|
||||
|
||||
private:
|
||||
virtual void avoid_weak_vtable();
|
||||
};
|
||||
|
||||
void dying_buffer::avoid_weak_vtable() {}
|
||||
|
||||
TEST(BufferTest, VirtualDtor) {
|
||||
typedef StrictMock<dying_buffer> stict_mock_buffer;
|
||||
stict_mock_buffer* mock_buffer = new stict_mock_buffer();
|
||||
EXPECT_CALL(*mock_buffer, die());
|
||||
buffer<int>* buffer = mock_buffer;
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
TEST(BufferTest, Access) {
|
||||
char data[10];
|
||||
mock_buffer<char> buffer(data, sizeof(data));
|
||||
|
@ -152,30 +126,40 @@ TEST(BufferTest, Access) {
|
|||
EXPECT_EQ(42, const_buffer[3]);
|
||||
}
|
||||
|
||||
TEST(BufferTest, Resize) {
|
||||
TEST(BufferTest, TryResize) {
|
||||
char data[123];
|
||||
mock_buffer<char> buffer(data, sizeof(data));
|
||||
buffer[10] = 42;
|
||||
EXPECT_EQ(42, buffer[10]);
|
||||
buffer.resize(20);
|
||||
buffer.try_resize(20);
|
||||
EXPECT_EQ(20u, buffer.size());
|
||||
EXPECT_EQ(123u, buffer.capacity());
|
||||
EXPECT_EQ(42, buffer[10]);
|
||||
buffer.resize(5);
|
||||
buffer.try_resize(5);
|
||||
EXPECT_EQ(5u, buffer.size());
|
||||
EXPECT_EQ(123u, buffer.capacity());
|
||||
EXPECT_EQ(42, buffer[10]);
|
||||
// Check if resize calls grow.
|
||||
// Check if try_resize calls grow.
|
||||
EXPECT_CALL(buffer, do_grow(124));
|
||||
buffer.resize(124);
|
||||
buffer.try_resize(124);
|
||||
EXPECT_CALL(buffer, do_grow(200));
|
||||
buffer.resize(200);
|
||||
buffer.try_resize(200);
|
||||
}
|
||||
|
||||
TEST(BufferTest, TryResizePartial) {
|
||||
char data[10];
|
||||
mock_buffer<char> buffer(data, sizeof(data));
|
||||
EXPECT_CALL(buffer, do_grow(20)).WillOnce(Return(15));
|
||||
buffer.try_resize(20);
|
||||
EXPECT_EQ(buffer.capacity(), 15);
|
||||
EXPECT_EQ(buffer.size(), 15);
|
||||
}
|
||||
|
||||
TEST(BufferTest, Clear) {
|
||||
test_buffer<char> buffer;
|
||||
buffer.resize(20);
|
||||
buffer.resize(0);
|
||||
mock_buffer<char> buffer;
|
||||
EXPECT_CALL(buffer, do_grow(20));
|
||||
buffer.try_resize(20);
|
||||
buffer.try_resize(0);
|
||||
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
|
||||
EXPECT_EQ(20u, buffer.capacity());
|
||||
}
|
||||
|
@ -183,11 +167,11 @@ TEST(BufferTest, Clear) {
|
|||
TEST(BufferTest, Append) {
|
||||
char data[15];
|
||||
mock_buffer<char> buffer(data, 10);
|
||||
const char* test = "test";
|
||||
auto test = "test";
|
||||
buffer.append(test, test + 5);
|
||||
EXPECT_STREQ(test, &buffer[0]);
|
||||
EXPECT_EQ(5u, buffer.size());
|
||||
buffer.resize(10);
|
||||
buffer.try_resize(10);
|
||||
EXPECT_CALL(buffer, do_grow(12));
|
||||
buffer.append(test, test + 2);
|
||||
EXPECT_EQ('t', buffer[10]);
|
||||
|
@ -195,17 +179,31 @@ TEST(BufferTest, Append) {
|
|||
EXPECT_EQ(12u, buffer.size());
|
||||
}
|
||||
|
||||
TEST(BufferTest, AppendPartial) {
|
||||
char data[10];
|
||||
mock_buffer<char> buffer(data, sizeof(data));
|
||||
testing::InSequence seq;
|
||||
EXPECT_CALL(buffer, do_grow(15)).WillOnce(Return(10));
|
||||
EXPECT_CALL(buffer, do_grow(15)).WillOnce(Invoke([&buffer](size_t) {
|
||||
EXPECT_EQ(fmt::string_view(buffer.data(), buffer.size()), "0123456789");
|
||||
buffer.clear();
|
||||
return 10;
|
||||
}));
|
||||
auto test = "0123456789abcde";
|
||||
buffer.append(test, test + 15);
|
||||
}
|
||||
|
||||
TEST(BufferTest, AppendAllocatesEnoughStorage) {
|
||||
char data[19];
|
||||
mock_buffer<char> buffer(data, 10);
|
||||
const char* test = "abcdefgh";
|
||||
buffer.resize(10);
|
||||
auto test = "abcdefgh";
|
||||
buffer.try_resize(10);
|
||||
EXPECT_CALL(buffer, do_grow(19));
|
||||
buffer.append(test, test + 9);
|
||||
}
|
||||
|
||||
TEST(ArgTest, FormatArgs) {
|
||||
fmt::format_args args;
|
||||
auto args = fmt::format_args();
|
||||
EXPECT_FALSE(args.get(1));
|
||||
}
|
||||
|
||||
|
@ -233,7 +231,7 @@ struct custom_context {
|
|||
};
|
||||
|
||||
TEST(ArgTest, MakeValueWithCustomContext) {
|
||||
test_struct t;
|
||||
auto t = test_struct();
|
||||
fmt::detail::value<custom_context> arg(
|
||||
fmt::detail::arg_mapper<custom_context>().map(t));
|
||||
custom_context ctx = {false, fmt::format_parse_context("")};
|
||||
|
@ -255,10 +253,10 @@ FMT_END_NAMESPACE
|
|||
struct test_result {};
|
||||
|
||||
template <typename T> struct mock_visitor {
|
||||
template <typename U> struct result { typedef test_result type; };
|
||||
template <typename U> struct result { using type = test_result; };
|
||||
|
||||
mock_visitor() {
|
||||
ON_CALL(*this, visit(_)).WillByDefault(testing::Return(test_result()));
|
||||
ON_CALL(*this, visit(_)).WillByDefault(Return(test_result()));
|
||||
}
|
||||
|
||||
MOCK_METHOD1_T(visit, test_result(T value));
|
||||
|
@ -272,10 +270,10 @@ template <typename T> struct mock_visitor {
|
|||
}
|
||||
};
|
||||
|
||||
template <typename T> struct visit_type { typedef T Type; };
|
||||
template <typename T> struct visit_type { using type = T; };
|
||||
|
||||
#define VISIT_TYPE(Type_, visit_type_) \
|
||||
template <> struct visit_type<Type_> { typedef visit_type_ Type; }
|
||||
#define VISIT_TYPE(type_, visit_type_) \
|
||||
template <> struct visit_type<type_> { using type = visit_type_; }
|
||||
|
||||
VISIT_TYPE(signed char, int);
|
||||
VISIT_TYPE(unsigned char, unsigned);
|
||||
|
@ -294,36 +292,34 @@ VISIT_TYPE(unsigned long, unsigned long long);
|
|||
{ \
|
||||
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
|
||||
EXPECT_CALL(visitor, visit(expected)); \
|
||||
typedef std::back_insert_iterator<buffer<Char>> iterator; \
|
||||
using iterator = std::back_insert_iterator<buffer<Char>>; \
|
||||
fmt::visit_format_arg( \
|
||||
visitor, make_arg<fmt::basic_format_context<iterator, Char>>(value)); \
|
||||
}
|
||||
|
||||
#define CHECK_ARG(value, typename_) \
|
||||
{ \
|
||||
typedef decltype(value) value_type; \
|
||||
typename_ visit_type<value_type>::Type expected = value; \
|
||||
using value_type = decltype(value); \
|
||||
typename_ visit_type<value_type>::type expected = value; \
|
||||
CHECK_ARG_(char, expected, value) \
|
||||
CHECK_ARG_(wchar_t, expected, value) \
|
||||
}
|
||||
|
||||
template <typename T> class NumericArgTest : public testing::Test {};
|
||||
|
||||
typedef ::testing::Types<bool, signed char, unsigned char, signed,
|
||||
unsigned short, int, unsigned, long, unsigned long,
|
||||
long long, unsigned long long, float, double,
|
||||
long double>
|
||||
Types;
|
||||
TYPED_TEST_CASE(NumericArgTest, Types);
|
||||
using types =
|
||||
::testing::Types<bool, signed char, unsigned char, signed, unsigned short,
|
||||
int, unsigned, long, unsigned long, long long,
|
||||
unsigned long long, float, double, long double>;
|
||||
TYPED_TEST_CASE(NumericArgTest, types);
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_integral<T>::value, T>::type test_value() {
|
||||
fmt::enable_if_t<std::is_integral<T>::value, T> test_value() {
|
||||
return static_cast<T>(42);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<std::is_floating_point<T>::value, T>::type
|
||||
test_value() {
|
||||
fmt::enable_if_t<std::is_floating_point<T>::value, T> test_value() {
|
||||
return static_cast<T>(4.2);
|
||||
}
|
||||
|
||||
|
@ -345,7 +341,7 @@ TEST(ArgTest, StringArg) {
|
|||
const char* cstr = str;
|
||||
CHECK_ARG_(char, cstr, str);
|
||||
|
||||
string_view sref(str);
|
||||
auto sref = string_view(str);
|
||||
CHECK_ARG_(char, sref, std::string(str));
|
||||
}
|
||||
|
||||
|
@ -372,14 +368,14 @@ TEST(ArgTest, PointerArg) {
|
|||
struct check_custom {
|
||||
test_result operator()(
|
||||
fmt::basic_format_arg<fmt::format_context>::handle h) const {
|
||||
struct test_buffer : fmt::detail::buffer<char> {
|
||||
struct test_buffer final : fmt::detail::buffer<char> {
|
||||
char data[10];
|
||||
test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {}
|
||||
void grow(size_t) {}
|
||||
} buffer;
|
||||
fmt::detail::buffer<char>& base = buffer;
|
||||
fmt::format_parse_context parse_ctx("");
|
||||
fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
|
||||
fmt::format_context ctx{fmt::detail::buffer_appender<char>(buffer),
|
||||
fmt::format_args()};
|
||||
h.format(parse_ctx, ctx);
|
||||
EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
|
||||
return test_result();
|
||||
|
@ -388,10 +384,10 @@ struct check_custom {
|
|||
|
||||
TEST(ArgTest, CustomArg) {
|
||||
test_struct test;
|
||||
typedef mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>
|
||||
visitor;
|
||||
using visitor =
|
||||
mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>;
|
||||
testing::StrictMock<visitor> v;
|
||||
EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom()));
|
||||
EXPECT_CALL(v, visit(_)).WillOnce(Invoke(check_custom()));
|
||||
fmt::visit_format_arg(v, make_arg<fmt::format_context>(test));
|
||||
}
|
||||
|
||||
|
@ -407,9 +403,7 @@ TEST(FormatDynArgsTest, Basic) {
|
|||
store.push_back(42);
|
||||
store.push_back("abc1");
|
||||
store.push_back(1.5f);
|
||||
|
||||
std::string result = fmt::vformat("{} and {} and {}", store);
|
||||
EXPECT_EQ("42 and abc1 and 1.5", result);
|
||||
EXPECT_EQ("42 and abc1 and 1.5", fmt::vformat("{} and {} and {}", store));
|
||||
}
|
||||
|
||||
TEST(FormatDynArgsTest, StringsAndRefs) {
|
||||
|
@ -451,7 +445,6 @@ TEST(FormatDynArgsTest, CustomFormat) {
|
|||
++c.i;
|
||||
store.push_back(std::cref(c));
|
||||
++c.i;
|
||||
|
||||
std::string result = fmt::vformat("{} and {} and {}", store);
|
||||
EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
|
||||
}
|
||||
|
@ -459,8 +452,7 @@ TEST(FormatDynArgsTest, CustomFormat) {
|
|||
TEST(FormatDynArgsTest, NamedInt) {
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
store.push_back(fmt::arg("a1", 42));
|
||||
std::string result = fmt::vformat("{a1}", store);
|
||||
EXPECT_EQ("42", result);
|
||||
EXPECT_EQ("42", fmt::vformat("{a1}", store));
|
||||
}
|
||||
|
||||
TEST(FormatDynArgsTest, NamedStrings) {
|
||||
|
@ -469,10 +461,7 @@ TEST(FormatDynArgsTest, NamedStrings) {
|
|||
store.push_back(fmt::arg("a1", str));
|
||||
store.push_back(fmt::arg("a2", std::cref(str)));
|
||||
str[0] = 'X';
|
||||
|
||||
std::string result = fmt::vformat("{a1} and {a2}", store);
|
||||
|
||||
EXPECT_EQ("1234567890 and X234567890", result);
|
||||
EXPECT_EQ("1234567890 and X234567890", fmt::vformat("{a1} and {a2}", store));
|
||||
}
|
||||
|
||||
TEST(FormatDynArgsTest, NamedArgByRef) {
|
||||
|
@ -494,7 +483,6 @@ TEST(FormatDynArgsTest, NamedArgByRef) {
|
|||
store.push_back(std::cref(a1));
|
||||
|
||||
std::string result = fmt::vformat("{a1_} and {} and {} and {}", store);
|
||||
|
||||
EXPECT_EQ("42 and abc and 1.5 and 42", result);
|
||||
}
|
||||
|
||||
|
@ -507,7 +495,6 @@ TEST(FormatDynArgsTest, NamedCustomFormat) {
|
|||
++c.i;
|
||||
store.push_back(fmt::arg("c_ref", std::cref(c)));
|
||||
++c.i;
|
||||
|
||||
std::string result = fmt::vformat("{c1} and {c2} and {c_ref}", store);
|
||||
EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
|
||||
}
|
||||
|
@ -663,14 +650,14 @@ TEST(CoreTest, FormatterOverridesImplicitConversion) {
|
|||
|
||||
namespace my_ns {
|
||||
template <typename Char> class my_string {
|
||||
private:
|
||||
std::basic_string<Char> s_;
|
||||
|
||||
public:
|
||||
my_string(const Char* s) : s_(s) {}
|
||||
const Char* data() const FMT_NOEXCEPT { return s_.data(); }
|
||||
size_t length() const FMT_NOEXCEPT { return s_.size(); }
|
||||
operator const Char*() const { return s_.c_str(); }
|
||||
|
||||
private:
|
||||
std::basic_string<Char> s_;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
|
@ -748,7 +735,7 @@ struct implicitly_convertible_to_string_view {
|
|||
operator fmt::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
TEST(FormatterTest, FormatImplicitlyConvertibleToStringView) {
|
||||
TEST(CoreTest, FormatImplicitlyConvertibleToStringView) {
|
||||
EXPECT_EQ("foo", fmt::format("{}", implicitly_convertible_to_string_view()));
|
||||
}
|
||||
|
||||
|
@ -758,7 +745,7 @@ struct explicitly_convertible_to_string_view {
|
|||
explicit operator fmt::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
|
||||
TEST(CoreTest, FormatExplicitlyConvertibleToStringView) {
|
||||
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view()));
|
||||
}
|
||||
|
||||
|
@ -767,7 +754,7 @@ struct explicitly_convertible_to_std_string_view {
|
|||
explicit operator std::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
|
||||
TEST(CoreTest, FormatExplicitlyConvertibleToStdStringView) {
|
||||
EXPECT_EQ("foo",
|
||||
fmt::format("{}", explicitly_convertible_to_std_string_view()));
|
||||
}
|
||||
|
@ -781,6 +768,6 @@ struct disabled_rvalue_conversion {
|
|||
operator const char*() && = delete;
|
||||
};
|
||||
|
||||
TEST(FormatterTest, DisabledRValueConversion) {
|
||||
TEST(CoreTest, DisabledRValueConversion) {
|
||||
EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion()));
|
||||
}
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
// Formatting library for C++ - custom argument formatter tests
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "gtest-extra.h"
|
||||
|
||||
// MSVC 2013 is known to be broken.
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||
|
||||
// A custom argument formatter that doesn't print `-` for floating-point values
|
||||
// rounded to 0.
|
||||
class custom_arg_formatter
|
||||
: public fmt::detail::arg_formatter<fmt::format_context::iterator, char> {
|
||||
public:
|
||||
using base = fmt::detail::arg_formatter<fmt::format_context::iterator, char>;
|
||||
|
||||
custom_arg_formatter(fmt::format_context& ctx,
|
||||
fmt::format_parse_context* parse_ctx,
|
||||
fmt::format_specs* s = nullptr,
|
||||
const char* = nullptr)
|
||||
: base(ctx, parse_ctx, s) {}
|
||||
|
||||
using base::operator();
|
||||
|
||||
iterator operator()(double value) {
|
||||
// Comparing a float to 0.0 is safe.
|
||||
if (round(value * pow(10, specs()->precision)) == 0.0) value = 0;
|
||||
return base::operator()(value);
|
||||
}
|
||||
};
|
||||
|
||||
std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::detail::buffer<char>& base = buffer;
|
||||
// Pass custom argument formatter as a template arg to vwrite.
|
||||
fmt::vformat_to<custom_arg_formatter>(std::back_inserter(base), format_str,
|
||||
args);
|
||||
return std::string(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
std::string custom_format(const char* format_str, const Args&... args) {
|
||||
auto va = fmt::make_format_args(args...);
|
||||
return custom_vformat(format_str, va);
|
||||
}
|
||||
|
||||
TEST(CustomFormatterTest, Format) {
|
||||
EXPECT_EQ("0.00", custom_format("{:.2f}", -.00001));
|
||||
}
|
||||
#endif
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.1.0)
|
||||
cmake_minimum_required(VERSION 3.1...3.18)
|
||||
|
||||
project(fmt-test)
|
||||
|
||||
|
|
|
@ -38,9 +38,9 @@ namespace std {
|
|||
|
||||
template<class Out, class charT> class basic_format_context;
|
||||
using format_context = basic_format_context<
|
||||
/* unspecified */ std::back_insert_iterator<fmt::detail::buffer<char>>, char>;
|
||||
/* unspecified */ fmt::detail::buffer_appender<char>, char>;
|
||||
using wformat_context = basic_format_context<
|
||||
/* unspecified */ std::back_insert_iterator<fmt::detail::buffer<wchar_t>>, wchar_t>;
|
||||
/* unspecified */ fmt::detail::buffer_appender<wchar_t>, wchar_t>;
|
||||
|
||||
template<class T, class charT = char> struct formatter {
|
||||
formatter() = delete;
|
||||
|
@ -714,7 +714,7 @@ string vformat(string_view fmt, format_args args) {
|
|||
fmt::detail::buffer<char>& buf = mbuf;
|
||||
using af = detail::arg_formatter<fmt::format_context::iterator, char>;
|
||||
detail::format_handler<af, char, format_context>
|
||||
h(std::back_inserter(buf), fmt, args, {});
|
||||
h(fmt::detail::buffer_appender<char>(buf), fmt, args, {});
|
||||
fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
|
||||
return to_string(mbuf);
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
// Copyright (c) 2020 Vladimir Solontsov
|
||||
// SPDX-License-Identifier: MIT Licence
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include "gtest-extra.h"
|
|
@ -19,7 +19,10 @@
|
|||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
||||
#undef max
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# undef max
|
||||
#endif
|
||||
|
||||
using fmt::detail::bigint;
|
||||
using fmt::detail::fp;
|
||||
|
@ -186,29 +189,6 @@ template <bool is_iec559> void run_double_tests() {
|
|||
template <> void run_double_tests<true>() {
|
||||
// Construct from double.
|
||||
EXPECT_EQ(fp(1.23), fp(0x13ae147ae147aeu, -52));
|
||||
|
||||
// Compute boundaries:
|
||||
fp value;
|
||||
// Normalized & not power of 2 - equidistant boundaries:
|
||||
auto b = value.assign_with_boundaries(1.23);
|
||||
EXPECT_EQ(value, fp(0x0013ae147ae147ae, -52));
|
||||
EXPECT_EQ(b.lower, 0x9d70a3d70a3d6c00);
|
||||
EXPECT_EQ(b.upper, 0x9d70a3d70a3d7400);
|
||||
// Normalized power of 2 - lower boundary is closer:
|
||||
b = value.assign_with_boundaries(1.9807040628566084e+28); // 2**94
|
||||
EXPECT_EQ(value, fp(0x0010000000000000, 42));
|
||||
EXPECT_EQ(b.lower, 0x7ffffffffffffe00);
|
||||
EXPECT_EQ(b.upper, 0x8000000000000400);
|
||||
// Smallest normalized double - equidistant boundaries:
|
||||
b = value.assign_with_boundaries(2.2250738585072014e-308);
|
||||
EXPECT_EQ(value, fp(0x0010000000000000, -1074));
|
||||
EXPECT_EQ(b.lower, 0x7ffffffffffffc00);
|
||||
EXPECT_EQ(b.upper, 0x8000000000000400);
|
||||
// Subnormal - equidistant boundaries:
|
||||
b = value.assign_with_boundaries(4.9406564584124654e-324);
|
||||
EXPECT_EQ(value, fp(0x0000000000000001, -1074));
|
||||
EXPECT_EQ(b.lower, 0x4000000000000000);
|
||||
EXPECT_EQ(b.upper, 0xc000000000000000);
|
||||
}
|
||||
|
||||
TEST(FPTest, DoubleTests) {
|
||||
|
@ -222,33 +202,6 @@ TEST(FPTest, Normalize) {
|
|||
EXPECT_EQ(-6, normalized.e);
|
||||
}
|
||||
|
||||
TEST(FPTest, ComputeFloatBoundaries) {
|
||||
struct {
|
||||
double x, lower, upper;
|
||||
} tests[] = {
|
||||
// regular
|
||||
{1.5f, 1.4999999403953552, 1.5000000596046448},
|
||||
// boundary
|
||||
{1.0f, 0.9999999701976776, 1.0000000596046448},
|
||||
// min normal
|
||||
{1.1754944e-38f, 1.1754942807573643e-38, 1.1754944208872107e-38},
|
||||
// max subnormal
|
||||
{1.1754942e-38f, 1.1754941406275179e-38, 1.1754942807573643e-38},
|
||||
// min subnormal
|
||||
{1e-45f, 7.006492321624085e-46, 2.1019476964872256e-45},
|
||||
};
|
||||
for (auto test : tests) {
|
||||
fp vlower = normalize(fp(test.lower));
|
||||
fp vupper = normalize(fp(test.upper));
|
||||
vlower.f >>= vupper.e - vlower.e;
|
||||
vlower.e = vupper.e;
|
||||
fp value;
|
||||
auto b = value.assign_float_with_boundaries(test.x);
|
||||
EXPECT_EQ(vlower.f, b.lower);
|
||||
EXPECT_EQ(vupper.f, b.upper);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FPTest, Multiply) {
|
||||
auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7);
|
||||
EXPECT_EQ(v.f, 123u * 56u);
|
||||
|
@ -259,17 +212,55 @@ TEST(FPTest, Multiply) {
|
|||
}
|
||||
|
||||
TEST(FPTest, GetCachedPower) {
|
||||
typedef std::numeric_limits<double> limits;
|
||||
using limits = std::numeric_limits<double>;
|
||||
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
|
||||
int dec_exp = 0;
|
||||
auto fp = fmt::detail::get_cached_power(exp, dec_exp);
|
||||
EXPECT_LE(exp, fp.e);
|
||||
int dec_exp_step = 8;
|
||||
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10));
|
||||
EXPECT_DOUBLE_EQ(pow(10, dec_exp), ldexp(static_cast<double>(fp.f), fp.e));
|
||||
bigint exact, cache(fp.f);
|
||||
if (dec_exp >= 0) {
|
||||
exact.assign_pow10(dec_exp);
|
||||
if (fp.e <= 0)
|
||||
exact <<= -fp.e;
|
||||
else
|
||||
cache <<= fp.e;
|
||||
exact.align(cache);
|
||||
cache.align(exact);
|
||||
auto exact_str = fmt::format("{}", exact);
|
||||
auto cache_str = fmt::format("{}", cache);
|
||||
EXPECT_EQ(exact_str.size(), cache_str.size());
|
||||
EXPECT_EQ(exact_str.substr(0, 15), cache_str.substr(0, 15));
|
||||
int diff = cache_str[15] - exact_str[15];
|
||||
if (diff == 1)
|
||||
EXPECT_GT(exact_str[16], '8');
|
||||
else
|
||||
EXPECT_EQ(diff, 0);
|
||||
} else {
|
||||
cache.assign_pow10(-dec_exp);
|
||||
cache *= fp.f + 1; // Inexact check.
|
||||
exact.assign(1);
|
||||
exact <<= -fp.e;
|
||||
exact.align(cache);
|
||||
auto exact_str = fmt::format("{}", exact);
|
||||
auto cache_str = fmt::format("{}", cache);
|
||||
EXPECT_EQ(exact_str.size(), cache_str.size());
|
||||
EXPECT_EQ(exact_str.substr(0, 16), cache_str.substr(0, 16));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FPTest, DragonboxMaxK) {
|
||||
using fmt::detail::dragonbox::floor_log10_pow2;
|
||||
using float_info = fmt::detail::dragonbox::float_info<float>;
|
||||
EXPECT_EQ(fmt::detail::const_check(float_info::max_k),
|
||||
float_info::kappa - floor_log10_pow2(float_info::min_exponent -
|
||||
float_info::significand_bits));
|
||||
using double_info = fmt::detail::dragonbox::float_info<double>;
|
||||
EXPECT_EQ(
|
||||
fmt::detail::const_check(double_info::max_k),
|
||||
double_info::kappa - floor_log10_pow2(double_info::min_exponent -
|
||||
double_info::significand_bits));
|
||||
}
|
||||
|
||||
TEST(FPTest, GetRoundDirection) {
|
||||
using fmt::detail::get_round_direction;
|
||||
using fmt::detail::round_direction;
|
||||
|
@ -307,7 +298,7 @@ TEST(FPTest, FixedHandler) {
|
|||
EXPECT_THROW(handler().on_digit('0', 100, 100, 0, exp, false),
|
||||
assertion_failure);
|
||||
namespace digits = fmt::detail::digits;
|
||||
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::done);
|
||||
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::error);
|
||||
// Check that divisor - error doesn't overflow.
|
||||
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error);
|
||||
// Check that 2 * error doesn't overflow.
|
||||
|
@ -349,14 +340,6 @@ TEST(FormatTest, ArgConverter) {
|
|||
EXPECT_EQ(value, fmt::visit_format_arg(value_extractor<long long>(), arg));
|
||||
}
|
||||
|
||||
TEST(FormatTest, FormatNegativeNaN) {
|
||||
double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
if (std::signbit(-nan))
|
||||
EXPECT_EQ("-nan", fmt::format("{}", -nan));
|
||||
else
|
||||
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
||||
}
|
||||
|
||||
TEST(FormatTest, StrError) {
|
||||
char* message = nullptr;
|
||||
char buffer[BUFFER_SIZE];
|
||||
|
@ -454,3 +437,10 @@ TEST(UtilTest, WriteFallbackUIntPtr) {
|
|||
fmt::detail::fallback_uintptr(reinterpret_cast<void*>(0xface)), nullptr);
|
||||
EXPECT_EQ(s, "0xface");
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
TEST(UtilTest, WriteConsoleSignature) {
|
||||
decltype(WriteConsoleW)* p = fmt::detail::WriteConsoleW;
|
||||
(void)p;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
// Check if fmt/format.h compiles with the X11 index macro defined.
|
||||
#define index(x, y) no nice things
|
||||
|
||||
#include "fmt/color.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
#undef index
|
||||
|
@ -147,6 +146,7 @@ TEST(IteratorTest, CountingIterator) {
|
|||
auto prev = it++;
|
||||
EXPECT_EQ(prev.count(), 0);
|
||||
EXPECT_EQ(it.count(), 1);
|
||||
EXPECT_EQ((it + 41).count(), 42);
|
||||
}
|
||||
|
||||
TEST(IteratorTest, TruncatingIterator) {
|
||||
|
@ -169,20 +169,22 @@ TEST(IteratorTest, TruncatingBackInserter) {
|
|||
}
|
||||
|
||||
TEST(IteratorTest, IsOutputIterator) {
|
||||
EXPECT_TRUE(fmt::detail::is_output_iterator<char*>::value);
|
||||
EXPECT_FALSE(fmt::detail::is_output_iterator<const char*>::value);
|
||||
EXPECT_FALSE(fmt::detail::is_output_iterator<std::string>::value);
|
||||
EXPECT_TRUE(fmt::detail::is_output_iterator<
|
||||
std::back_insert_iterator<std::string>>::value);
|
||||
EXPECT_TRUE(fmt::detail::is_output_iterator<std::string::iterator>::value);
|
||||
EXPECT_FALSE(
|
||||
fmt::detail::is_output_iterator<std::string::const_iterator>::value);
|
||||
EXPECT_FALSE(fmt::detail::is_output_iterator<std::list<char>>::value);
|
||||
EXPECT_TRUE((fmt::detail::is_output_iterator<char*, char>::value));
|
||||
EXPECT_FALSE((fmt::detail::is_output_iterator<const char*, char>::value));
|
||||
EXPECT_FALSE((fmt::detail::is_output_iterator<std::string, char>::value));
|
||||
EXPECT_TRUE(
|
||||
fmt::detail::is_output_iterator<std::list<char>::iterator>::value);
|
||||
EXPECT_FALSE(
|
||||
fmt::detail::is_output_iterator<std::list<char>::const_iterator>::value);
|
||||
EXPECT_FALSE(fmt::detail::is_output_iterator<uint32_pair>::value);
|
||||
(fmt::detail::is_output_iterator<std::back_insert_iterator<std::string>,
|
||||
char>::value));
|
||||
EXPECT_TRUE(
|
||||
(fmt::detail::is_output_iterator<std::string::iterator, char>::value));
|
||||
EXPECT_FALSE((fmt::detail::is_output_iterator<std::string::const_iterator,
|
||||
char>::value));
|
||||
EXPECT_FALSE((fmt::detail::is_output_iterator<std::list<char>, char>::value));
|
||||
EXPECT_TRUE((
|
||||
fmt::detail::is_output_iterator<std::list<char>::iterator, char>::value));
|
||||
EXPECT_FALSE((fmt::detail::is_output_iterator<std::list<char>::const_iterator,
|
||||
char>::value));
|
||||
EXPECT_FALSE((fmt::detail::is_output_iterator<uint32_pair, char>::value));
|
||||
}
|
||||
|
||||
TEST(MemoryBufferTest, Ctor) {
|
||||
|
@ -236,7 +238,7 @@ TEST(MemoryBufferTest, MoveCtorInlineBuffer) {
|
|||
std::allocator<char> alloc;
|
||||
basic_memory_buffer<char, 5, TestAllocator> buffer((TestAllocator(&alloc)));
|
||||
const char test[] = "test";
|
||||
buffer.append(test, test + 4);
|
||||
buffer.append(string_view(test, 4));
|
||||
check_move_buffer("test", buffer);
|
||||
// Adding one more character fills the inline buffer, but doesn't cause
|
||||
// dynamic allocation.
|
||||
|
@ -293,12 +295,8 @@ TEST(MemoryBufferTest, MoveAssignment) {
|
|||
|
||||
TEST(MemoryBufferTest, Grow) {
|
||||
typedef allocator_ref<mock_allocator<int>> Allocator;
|
||||
typedef basic_memory_buffer<int, 10, Allocator> Base;
|
||||
mock_allocator<int> alloc;
|
||||
struct TestMemoryBuffer : Base {
|
||||
TestMemoryBuffer(Allocator alloc) : Base(alloc) {}
|
||||
void grow(size_t size) { Base::grow(size); }
|
||||
} buffer((Allocator(&alloc)));
|
||||
basic_memory_buffer<int, 10, Allocator> buffer((Allocator(&alloc)));
|
||||
buffer.resize(7);
|
||||
using fmt::detail::to_unsigned;
|
||||
for (int i = 0; i < 7; ++i) buffer[to_unsigned(i)] = i * i;
|
||||
|
@ -306,7 +304,7 @@ TEST(MemoryBufferTest, Grow) {
|
|||
int mem[20];
|
||||
mem[7] = 0xdead;
|
||||
EXPECT_CALL(alloc, allocate(20)).WillOnce(Return(mem));
|
||||
buffer.grow(20);
|
||||
buffer.try_reserve(20);
|
||||
EXPECT_EQ(20u, buffer.capacity());
|
||||
// Check if size elements have been copied
|
||||
for (int i = 0; i < 7; ++i) EXPECT_EQ(i * i, buffer[to_unsigned(i)]);
|
||||
|
@ -582,8 +580,8 @@ TEST(FormatterTest, LeftAlign) {
|
|||
EXPECT_EQ("42 ", format("{0:<5}", 42ul));
|
||||
EXPECT_EQ("-42 ", format("{0:<5}", -42ll));
|
||||
EXPECT_EQ("42 ", format("{0:<5}", 42ull));
|
||||
EXPECT_EQ("-42.0 ", format("{0:<7}", -42.0));
|
||||
EXPECT_EQ("-42.0 ", format("{0:<7}", -42.0l));
|
||||
EXPECT_EQ("-42 ", format("{0:<5}", -42.0));
|
||||
EXPECT_EQ("-42 ", format("{0:<5}", -42.0l));
|
||||
EXPECT_EQ("c ", format("{0:<5}", 'c'));
|
||||
EXPECT_EQ("abc ", format("{0:<5}", "abc"));
|
||||
EXPECT_EQ("0xface ", format("{0:<8}", reinterpret_cast<void*>(0xface)));
|
||||
|
@ -599,8 +597,8 @@ TEST(FormatterTest, RightAlign) {
|
|||
EXPECT_EQ(" 42", format("{0:>5}", 42ul));
|
||||
EXPECT_EQ(" -42", format("{0:>5}", -42ll));
|
||||
EXPECT_EQ(" 42", format("{0:>5}", 42ull));
|
||||
EXPECT_EQ(" -42.0", format("{0:>7}", -42.0));
|
||||
EXPECT_EQ(" -42.0", format("{0:>7}", -42.0l));
|
||||
EXPECT_EQ(" -42", format("{0:>5}", -42.0));
|
||||
EXPECT_EQ(" -42", format("{0:>5}", -42.0l));
|
||||
EXPECT_EQ(" c", format("{0:>5}", 'c'));
|
||||
EXPECT_EQ(" abc", format("{0:>5}", "abc"));
|
||||
EXPECT_EQ(" 0xface", format("{0:>8}", reinterpret_cast<void*>(0xface)));
|
||||
|
@ -620,8 +618,8 @@ TEST(FormatterTest, CenterAlign) {
|
|||
EXPECT_EQ(" 42 ", format("{0:^5}", 42ul));
|
||||
EXPECT_EQ(" -42 ", format("{0:^5}", -42ll));
|
||||
EXPECT_EQ(" 42 ", format("{0:^5}", 42ull));
|
||||
EXPECT_EQ(" -42.0 ", format("{0:^7}", -42.0));
|
||||
EXPECT_EQ(" -42.0 ", format("{0:^7}", -42.0l));
|
||||
EXPECT_EQ(" -42 ", format("{0:^5}", -42.0));
|
||||
EXPECT_EQ(" -42 ", format("{0:^5}", -42.0l));
|
||||
EXPECT_EQ(" c ", format("{0:^5}", 'c'));
|
||||
EXPECT_EQ(" abc ", format("{0:^6}", "abc"));
|
||||
EXPECT_EQ(" 0xface ", format("{0:^8}", reinterpret_cast<void*>(0xface)));
|
||||
|
@ -639,8 +637,8 @@ TEST(FormatterTest, Fill) {
|
|||
EXPECT_EQ("***42", format("{0:*>5}", 42ul));
|
||||
EXPECT_EQ("**-42", format("{0:*>5}", -42ll));
|
||||
EXPECT_EQ("***42", format("{0:*>5}", 42ull));
|
||||
EXPECT_EQ("**-42.0", format("{0:*>7}", -42.0));
|
||||
EXPECT_EQ("**-42.0", format("{0:*>7}", -42.0l));
|
||||
EXPECT_EQ("**-42", format("{0:*>5}", -42.0));
|
||||
EXPECT_EQ("**-42", format("{0:*>5}", -42.0l));
|
||||
EXPECT_EQ("c****", format("{0:*<5}", 'c'));
|
||||
EXPECT_EQ("abc**", format("{0:*<5}", "abc"));
|
||||
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
|
||||
|
@ -648,7 +646,7 @@ TEST(FormatterTest, Fill) {
|
|||
EXPECT_EQ(std::string("\0\0\0*", 4), format(string_view("{:\0>4}", 6), '*'));
|
||||
EXPECT_EQ("жж42", format("{0:ж>4}", 42));
|
||||
EXPECT_THROW_MSG(format("{:\x80\x80\x80\x80\x80>}", 0), format_error,
|
||||
"invalid fill");
|
||||
"missing '}' in format string");
|
||||
}
|
||||
|
||||
TEST(FormatterTest, PlusSign) {
|
||||
|
@ -663,8 +661,8 @@ TEST(FormatterTest, PlusSign) {
|
|||
EXPECT_EQ("+42", format("{0:+}", 42ll));
|
||||
EXPECT_THROW_MSG(format("{0:+}", 42ull), format_error,
|
||||
"format specifier requires signed argument");
|
||||
EXPECT_EQ("+42.0", format("{0:+}", 42.0));
|
||||
EXPECT_EQ("+42.0", format("{0:+}", 42.0l));
|
||||
EXPECT_EQ("+42", format("{0:+}", 42.0));
|
||||
EXPECT_EQ("+42", format("{0:+}", 42.0l));
|
||||
EXPECT_THROW_MSG(format("{0:+", 'c'), format_error,
|
||||
"missing '}' in format string");
|
||||
EXPECT_THROW_MSG(format("{0:+}", 'c'), format_error,
|
||||
|
@ -687,8 +685,8 @@ TEST(FormatterTest, MinusSign) {
|
|||
EXPECT_EQ("42", format("{0:-}", 42ll));
|
||||
EXPECT_THROW_MSG(format("{0:-}", 42ull), format_error,
|
||||
"format specifier requires signed argument");
|
||||
EXPECT_EQ("42.0", format("{0:-}", 42.0));
|
||||
EXPECT_EQ("42.0", format("{0:-}", 42.0l));
|
||||
EXPECT_EQ("42", format("{0:-}", 42.0));
|
||||
EXPECT_EQ("42", format("{0:-}", 42.0l));
|
||||
EXPECT_THROW_MSG(format("{0:-", 'c'), format_error,
|
||||
"missing '}' in format string");
|
||||
EXPECT_THROW_MSG(format("{0:-}", 'c'), format_error,
|
||||
|
@ -711,8 +709,8 @@ TEST(FormatterTest, SpaceSign) {
|
|||
EXPECT_EQ(" 42", format("{0: }", 42ll));
|
||||
EXPECT_THROW_MSG(format("{0: }", 42ull), format_error,
|
||||
"format specifier requires signed argument");
|
||||
EXPECT_EQ(" 42.0", format("{0: }", 42.0));
|
||||
EXPECT_EQ(" 42.0", format("{0: }", 42.0l));
|
||||
EXPECT_EQ(" 42", format("{0: }", 42.0));
|
||||
EXPECT_EQ(" 42", format("{0: }", 42.0l));
|
||||
EXPECT_THROW_MSG(format("{0: ", 'c'), format_error,
|
||||
"missing '}' in format string");
|
||||
EXPECT_THROW_MSG(format("{0: }", 'c'), format_error,
|
||||
|
@ -723,6 +721,12 @@ TEST(FormatterTest, SpaceSign) {
|
|||
"format specifier requires numeric argument");
|
||||
}
|
||||
|
||||
TEST(FormatterTest, SignNotTruncated) {
|
||||
wchar_t format_str[] = {L'{', L':',
|
||||
'+' | (1 << fmt::detail::num_bits<char>()), L'}', 0};
|
||||
EXPECT_THROW(format(format_str, 42), format_error);
|
||||
}
|
||||
|
||||
TEST(FormatterTest, HashFlag) {
|
||||
EXPECT_EQ("42", format("{0:#}", 42));
|
||||
EXPECT_EQ("-42", format("{0:#}", -42));
|
||||
|
@ -761,8 +765,8 @@ TEST(FormatterTest, HashFlag) {
|
|||
EXPECT_EQ("-42.0", format("{0:#}", -42.0l));
|
||||
EXPECT_EQ("4.e+01", format("{:#.0e}", 42.0));
|
||||
EXPECT_EQ("0.", format("{:#.0f}", 0.01));
|
||||
auto s = format("{:#.0f}", 0.5); // MSVC's printf uses wrong rounding mode.
|
||||
EXPECT_TRUE(s == "0." || s == "1.");
|
||||
EXPECT_EQ("0.50", format("{:#.2g}", 0.5));
|
||||
EXPECT_EQ("0.", format("{:#.0f}", 0.5));
|
||||
EXPECT_THROW_MSG(format("{0:#", 'c'), format_error,
|
||||
"missing '}' in format string");
|
||||
EXPECT_THROW_MSG(format("{0:#}", 'c'), format_error,
|
||||
|
@ -781,8 +785,8 @@ TEST(FormatterTest, ZeroFlag) {
|
|||
EXPECT_EQ("00042", format("{0:05}", 42ul));
|
||||
EXPECT_EQ("-0042", format("{0:05}", -42ll));
|
||||
EXPECT_EQ("00042", format("{0:05}", 42ull));
|
||||
EXPECT_EQ("-0042.0", format("{0:07}", -42.0));
|
||||
EXPECT_EQ("-0042.0", format("{0:07}", -42.0l));
|
||||
EXPECT_EQ("-000042", format("{0:07}", -42.0));
|
||||
EXPECT_EQ("-000042", format("{0:07}", -42.0l));
|
||||
EXPECT_THROW_MSG(format("{0:0", 'c'), format_error,
|
||||
"missing '}' in format string");
|
||||
EXPECT_THROW_MSG(format("{0:05}", 'c'), format_error,
|
||||
|
@ -955,6 +959,9 @@ TEST(FormatterTest, Precision) {
|
|||
EXPECT_EQ("123.", format("{:#.0f}", 123.0));
|
||||
EXPECT_EQ("1.23", format("{:.02f}", 1.234));
|
||||
EXPECT_EQ("0.001", format("{:.1g}", 0.001));
|
||||
EXPECT_EQ("1019666400", format("{}", 1019666432.0f));
|
||||
EXPECT_EQ("1e+01", format("{:.0e}", 9.5));
|
||||
EXPECT_EQ("1.0e-34", fmt::format("{:.1e}", 1e-34));
|
||||
|
||||
EXPECT_THROW_MSG(format("{0:.2}", reinterpret_cast<void*>(0xcafe)),
|
||||
format_error,
|
||||
|
@ -1236,17 +1243,20 @@ TEST(FormatterTest, FormatConvertibleToLongLong) {
|
|||
}
|
||||
|
||||
TEST(FormatterTest, FormatFloat) {
|
||||
EXPECT_EQ("0", format("{}", 0.0f));
|
||||
EXPECT_EQ("392.500000", format("{0:f}", 392.5f));
|
||||
}
|
||||
|
||||
TEST(FormatterTest, FormatDouble) {
|
||||
EXPECT_EQ("0", format("{}", 0.0));
|
||||
check_unknown_types(1.2, "eEfFgGaAnL%", "double");
|
||||
EXPECT_EQ("0.0", format("{:}", 0.0));
|
||||
EXPECT_EQ("0", format("{:}", 0.0));
|
||||
EXPECT_EQ("0.000000", format("{:f}", 0.0));
|
||||
EXPECT_EQ("0", format("{:g}", 0.0));
|
||||
EXPECT_EQ("392.65", format("{:}", 392.65));
|
||||
EXPECT_EQ("392.65", format("{:g}", 392.65));
|
||||
EXPECT_EQ("392.65", format("{:G}", 392.65));
|
||||
EXPECT_EQ("4.9014e+06", format("{:g}", 4.9014e6));
|
||||
EXPECT_EQ("392.650000", format("{:f}", 392.65));
|
||||
EXPECT_EQ("392.650000", format("{:F}", 392.65));
|
||||
EXPECT_EQ("42", format("{:L}", 42.0));
|
||||
|
@ -1260,6 +1270,8 @@ TEST(FormatterTest, FormatDouble) {
|
|||
EXPECT_EQ(buffer, format("{:a}", -42.0));
|
||||
safe_sprintf(buffer, "%A", -42.0);
|
||||
EXPECT_EQ(buffer, format("{:A}", -42.0));
|
||||
EXPECT_EQ("9223372036854775808.000000",
|
||||
format("{:f}", 9223372036854775807.0));
|
||||
}
|
||||
|
||||
TEST(FormatterTest, PrecisionRounding) {
|
||||
|
@ -1273,17 +1285,42 @@ TEST(FormatterTest, PrecisionRounding) {
|
|||
EXPECT_EQ("1.000", format("{:.3f}", 0.9999));
|
||||
EXPECT_EQ("0.00123", format("{:.3}", 0.00123));
|
||||
EXPECT_EQ("0.1", format("{:.16g}", 0.1));
|
||||
// Trigger rounding error in Grisu by a carefully chosen number.
|
||||
auto n = 3788512123356.985352;
|
||||
char buffer[64];
|
||||
safe_sprintf(buffer, "%f", n);
|
||||
EXPECT_EQ(buffer, format("{:f}", n));
|
||||
EXPECT_EQ("1", fmt::format("{:.0}", 1.0));
|
||||
EXPECT_EQ("225.51575035152063720",
|
||||
fmt::format("{:.17f}", 225.51575035152064));
|
||||
EXPECT_EQ("-761519619559038.2", fmt::format("{:.1f}", -761519619559038.2));
|
||||
EXPECT_EQ("1.9156918820264798e-56",
|
||||
fmt::format("{}", 1.9156918820264798e-56));
|
||||
EXPECT_EQ("0.0000", fmt::format("{:.4f}", 7.2809479766055470e-15));
|
||||
|
||||
// Trigger a rounding error in Grisu by a specially chosen number.
|
||||
EXPECT_EQ("3788512123356.985352", format("{:f}", 3788512123356.985352));
|
||||
}
|
||||
|
||||
TEST(FormatterTest, PrettifyFloat) {
|
||||
EXPECT_EQ("0.0001", fmt::format("{}", 1e-4));
|
||||
EXPECT_EQ("1e-05", fmt::format("{}", 1e-5));
|
||||
EXPECT_EQ("1000000000000000", fmt::format("{}", 1e15));
|
||||
EXPECT_EQ("1e+16", fmt::format("{}", 1e16));
|
||||
EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5));
|
||||
EXPECT_EQ("10000000000", fmt::format("{}", 1e10));
|
||||
EXPECT_EQ("100000000000", fmt::format("{}", 1e11));
|
||||
EXPECT_EQ("12340000000", fmt::format("{}", 1234e7));
|
||||
EXPECT_EQ("12.34", fmt::format("{}", 1234e-2));
|
||||
EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6));
|
||||
EXPECT_EQ("0.1", fmt::format("{}", 0.1f));
|
||||
EXPECT_EQ("0.10000000149011612", fmt::format("{}", double(0.1f)));
|
||||
EXPECT_EQ("1.3563156e-19", fmt::format("{}", 1.35631564e-19f));
|
||||
}
|
||||
|
||||
TEST(FormatterTest, FormatNaN) {
|
||||
double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
EXPECT_EQ("nan", format("{}", nan));
|
||||
EXPECT_EQ("+nan", format("{:+}", nan));
|
||||
if (std::signbit(-nan))
|
||||
EXPECT_EQ("-nan", format("{}", -nan));
|
||||
else
|
||||
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
||||
EXPECT_EQ(" nan", format("{: }", nan));
|
||||
EXPECT_EQ("NAN", format("{:F}", nan));
|
||||
EXPECT_EQ("nan ", format("{:<7}", nan));
|
||||
|
@ -1304,7 +1341,7 @@ TEST(FormatterTest, FormatInfinity) {
|
|||
}
|
||||
|
||||
TEST(FormatterTest, FormatLongDouble) {
|
||||
EXPECT_EQ("0.0", format("{0:}", 0.0l));
|
||||
EXPECT_EQ("0", format("{0:}", 0.0l));
|
||||
EXPECT_EQ("0.000000", format("{0:f}", 0.0l));
|
||||
EXPECT_EQ("392.65", format("{0:}", 392.65l));
|
||||
EXPECT_EQ("392.65", format("{0:g}", 392.65l));
|
||||
|
@ -1521,6 +1558,7 @@ TEST(FormatterTest, WideFormatString) {
|
|||
EXPECT_EQ(L"4.2", format(L"{}", 4.2));
|
||||
EXPECT_EQ(L"abc", format(L"{}", L"abc"));
|
||||
EXPECT_EQ(L"z", format(L"{}", L'z'));
|
||||
EXPECT_THROW(fmt::format(L"{:*\x343E}", 42), fmt::format_error);
|
||||
}
|
||||
|
||||
TEST(FormatterTest, FormatStringFromSpeedTest) {
|
||||
|
@ -1806,59 +1844,6 @@ TEST(FormatTest, StrongEnum) {
|
|||
}
|
||||
#endif
|
||||
|
||||
using buffer_iterator = fmt::format_context::iterator;
|
||||
|
||||
class mock_arg_formatter
|
||||
: public fmt::detail::arg_formatter_base<buffer_iterator, char> {
|
||||
private:
|
||||
#if FMT_USE_INT128
|
||||
MOCK_METHOD1(call, void(__int128_t value));
|
||||
#else
|
||||
MOCK_METHOD1(call, void(long long value));
|
||||
#endif
|
||||
|
||||
public:
|
||||
using base = fmt::detail::arg_formatter_base<buffer_iterator, char>;
|
||||
|
||||
mock_arg_formatter(fmt::format_context& ctx, fmt::format_parse_context*,
|
||||
fmt::format_specs* s = nullptr, const char* = nullptr)
|
||||
: base(ctx.out(), s, ctx.locale()) {
|
||||
EXPECT_CALL(*this, call(42));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<fmt::detail::is_integral<T>::value, iterator>::type
|
||||
operator()(T value) {
|
||||
call(value);
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<!fmt::detail::is_integral<T>::value, iterator>::type
|
||||
operator()(T value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
iterator operator()(fmt::basic_format_arg<fmt::format_context>::handle) {
|
||||
return base::operator()(fmt::monostate());
|
||||
}
|
||||
};
|
||||
|
||||
static void custom_vformat(fmt::string_view format_str, fmt::format_args args) {
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::detail::buffer<char>& base = buffer;
|
||||
fmt::vformat_to<mock_arg_formatter>(std::back_inserter(base), format_str,
|
||||
args);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void custom_format(const char* format_str, const Args&... args) {
|
||||
auto va = fmt::make_format_args(args...);
|
||||
return custom_vformat(format_str, va);
|
||||
}
|
||||
|
||||
TEST(FormatTest, CustomArgFormatter) { custom_format("{}", 42); }
|
||||
|
||||
TEST(FormatTest, NonNullTerminatedFormatString) {
|
||||
EXPECT_EQ("42", format(string_view("{}foo", 2), 42));
|
||||
}
|
||||
|
@ -1947,6 +1932,12 @@ TEST(FormatTest, FormattedSize) {
|
|||
EXPECT_EQ(2u, fmt::formatted_size("{}", 42));
|
||||
}
|
||||
|
||||
TEST(FormatTest, FormatTo) {
|
||||
std::vector<char> v;
|
||||
fmt::format_to(std::back_inserter(v), "{}", "foo");
|
||||
EXPECT_EQ(string_view(v.data(), v.size()), "foo");
|
||||
}
|
||||
|
||||
TEST(FormatTest, FormatToN) {
|
||||
char buffer[4];
|
||||
buffer[3] = 'x';
|
||||
|
@ -1954,10 +1945,12 @@ TEST(FormatTest, FormatToN) {
|
|||
EXPECT_EQ(5u, result.size);
|
||||
EXPECT_EQ(buffer + 3, result.out);
|
||||
EXPECT_EQ("123x", fmt::string_view(buffer, 4));
|
||||
|
||||
result = fmt::format_to_n(buffer, 3, "{:s}", "foobar");
|
||||
EXPECT_EQ(6u, result.size);
|
||||
EXPECT_EQ(buffer + 3, result.out);
|
||||
EXPECT_EQ("foox", fmt::string_view(buffer, 4));
|
||||
|
||||
buffer[0] = 'x';
|
||||
buffer[1] = 'x';
|
||||
buffer[2] = 'x';
|
||||
|
@ -1965,10 +1958,20 @@ TEST(FormatTest, FormatToN) {
|
|||
EXPECT_EQ(1u, result.size);
|
||||
EXPECT_EQ(buffer + 1, result.out);
|
||||
EXPECT_EQ("Axxx", fmt::string_view(buffer, 4));
|
||||
|
||||
result = fmt::format_to_n(buffer, 3, "{}{} ", 'B', 'C');
|
||||
EXPECT_EQ(3u, result.size);
|
||||
EXPECT_EQ(buffer + 3, result.out);
|
||||
EXPECT_EQ("BC x", fmt::string_view(buffer, 4));
|
||||
|
||||
result = fmt::format_to_n(buffer, 4, "{}", "ABCDE");
|
||||
EXPECT_EQ(5u, result.size);
|
||||
EXPECT_EQ("ABCD", fmt::string_view(buffer, 4));
|
||||
|
||||
buffer[3] = 'x';
|
||||
result = fmt::format_to_n(buffer, 3, "{}", std::string(1000, '*'));
|
||||
EXPECT_EQ(1000u, result.size);
|
||||
EXPECT_EQ("***x", fmt::string_view(buffer, 4));
|
||||
}
|
||||
|
||||
TEST(FormatTest, WideFormatToN) {
|
||||
|
@ -2413,12 +2416,6 @@ TEST(FormatTest, FmtStringInTemplate) {
|
|||
|
||||
#endif // FMT_USE_CONSTEXPR
|
||||
|
||||
TEST(FormatTest, EmphasisNonHeaderOnly) {
|
||||
// Ensure this compiles even if FMT_HEADER_ONLY is not defined.
|
||||
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"),
|
||||
"\x1b[1mbold error\x1b[0m");
|
||||
}
|
||||
|
||||
TEST(FormatTest, CharTraitsIsNotAmbiguous) {
|
||||
// Test that we don't inject detail names into the std namespace.
|
||||
using namespace std;
|
||||
|
@ -2431,25 +2428,30 @@ TEST(FormatTest, CharTraitsIsNotAmbiguous) {
|
|||
#endif
|
||||
}
|
||||
|
||||
struct mychar {
|
||||
#if __cplusplus > 201103L
|
||||
struct custom_char {
|
||||
int value;
|
||||
mychar() = default;
|
||||
custom_char() = default;
|
||||
|
||||
template <typename T> mychar(T val) : value(static_cast<int>(val)) {}
|
||||
template <typename T>
|
||||
constexpr custom_char(T val) : value(static_cast<int>(val)) {}
|
||||
|
||||
operator int() const { return value; }
|
||||
};
|
||||
|
||||
int to_ascii(custom_char c) { return c; }
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <> struct is_char<mychar> : std::true_type {};
|
||||
template <> struct is_char<custom_char> : std::true_type {};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(FormatTest, FormatCustomChar) {
|
||||
const mychar format[] = {'{', '}', 0};
|
||||
auto result = fmt::format(format, mychar('x'));
|
||||
const custom_char format[] = {'{', '}', 0};
|
||||
auto result = fmt::format(format, custom_char('x'));
|
||||
EXPECT_EQ(result.size(), 1);
|
||||
EXPECT_EQ(result[0], mychar('x'));
|
||||
EXPECT_EQ(result[0], custom_char('x'));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Convert a char8_t string to std::string. Otherwise GTest will insist on
|
||||
// inserting `char8_t` NTBS into a `char` stream which is disabled by P1423.
|
||||
|
@ -2467,3 +2469,27 @@ TEST(FormatTest, FormatUTF8Precision) {
|
|||
EXPECT_EQ(result.size(), 5);
|
||||
EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5)));
|
||||
}
|
||||
|
||||
struct check_back_appender {};
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <> struct formatter<check_back_appender> {
|
||||
template <typename ParseContext>
|
||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename Context>
|
||||
auto format(check_back_appender, Context& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
static_assert(std::is_same<decltype(++out), decltype(out)&>::value,
|
||||
"needs to satisfy weakly_incrementable");
|
||||
*out = 'y';
|
||||
return ++out;
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(FormatTest, BackInsertSlicing) {
|
||||
EXPECT_EQ(fmt::format("{}", check_back_appender{}), "y");
|
||||
}
|
||||
|
|
|
@ -1,38 +1,30 @@
|
|||
# Copyright (c) 2019, Paul Dreik
|
||||
# License: see LICENSE.rst in the fmt root directory
|
||||
|
||||
# settings this links in a main. useful for reproducing,
|
||||
# kcov, gdb, afl, valgrind.
|
||||
# (note that libFuzzer can also reproduce, just pass it the files)
|
||||
option(FMT_FUZZ_LINKMAIN "enables the reproduce mode, instead of libFuzzer" On)
|
||||
# Link in the main function. Useful for reproducing, kcov, gdb, afl, valgrind.
|
||||
# (Note that libFuzzer can also reproduce, just pass it the files.)
|
||||
option(FMT_FUZZ_LINKMAIN "Enables the reproduce mode, instead of libFuzzer" On)
|
||||
|
||||
# For oss-fuzz - insert $LIB_FUZZING_ENGINE into the link flags, but only for
|
||||
# the fuzz targets, otherwise the cmake configuration step fails.
|
||||
# the fuzz targets, otherwise the CMake configuration step fails.
|
||||
set(FMT_FUZZ_LDFLAGS "" CACHE STRING "LDFLAGS for the fuzz targets")
|
||||
|
||||
# Find all fuzzers.
|
||||
set(SOURCES
|
||||
chrono_duration.cpp
|
||||
named_arg.cpp
|
||||
one_arg.cpp
|
||||
sprintf.cpp
|
||||
two_args.cpp
|
||||
)
|
||||
|
||||
macro(implement_fuzzer sourcefile)
|
||||
get_filename_component(basename ${sourcefile} NAME_WE)
|
||||
set(name fuzzer_${basename})
|
||||
add_executable(${name} ${sourcefile} fuzzer_common.h)
|
||||
# Adds a binary for reproducing, i.e. no fuzzing, just enables replaying data
|
||||
# through the fuzzers.
|
||||
function(add_fuzzer source)
|
||||
get_filename_component(basename ${source} NAME_WE)
|
||||
set(name ${basename}-fuzzer)
|
||||
add_executable(${name} ${source} fuzzer-common.h)
|
||||
if (FMT_FUZZ_LINKMAIN)
|
||||
target_sources(${name} PRIVATE main.cpp)
|
||||
target_sources(${name} PRIVATE main.cc)
|
||||
endif ()
|
||||
target_link_libraries(${name} PRIVATE fmt)
|
||||
if (FMT_FUZZ_LDFLAGS)
|
||||
target_link_libraries(${name} PRIVATE ${FMT_FUZZ_LDFLAGS})
|
||||
endif ()
|
||||
if (FMT_FUZZ_LDFLAGS)
|
||||
target_link_libraries(${name} PRIVATE ${FMT_FUZZ_LDFLAGS})
|
||||
endif ()
|
||||
target_compile_features(${name} PRIVATE cxx_generic_lambdas)
|
||||
endmacro ()
|
||||
endfunction()
|
||||
|
||||
foreach (X IN ITEMS ${SOURCES})
|
||||
implement_fuzzer(${X})
|
||||
foreach (source chrono-duration.cc float.cc named-arg.cc one-arg.cc two-args.cc)
|
||||
add_fuzzer(${source})
|
||||
endforeach ()
|
||||
|
|
|
@ -1,27 +1,4 @@
|
|||
# FMT Fuzzer
|
||||
|
||||
Fuzzing has revealed [several bugs](https://github.com/fmtlib/fmt/issues?&q=is%3Aissue+fuzz)
|
||||
in fmt. It is a part of the continous fuzzing at
|
||||
[oss-fuzz](https://github.com/google/oss-fuzz).
|
||||
|
||||
The source code is modified to make the fuzzing possible without locking up on
|
||||
resource exhaustion:
|
||||
```cpp
|
||||
#ifdef FMT_FUZZ
|
||||
if(spec.precision>100000) {
|
||||
throw std::runtime_error("fuzz mode - avoiding large precision");
|
||||
}
|
||||
#endif
|
||||
```
|
||||
This macro `FMT_FUZZ` is enabled on OSS-Fuzz builds and makes fuzzing
|
||||
practically possible. It is used in fmt code to prevent resource exhaustion in
|
||||
fuzzing mode.
|
||||
The macro `FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` is the
|
||||
defacto standard for making fuzzing practically possible to disable certain
|
||||
fuzzing-unfriendly features (for example, randomness), see [the libFuzzer
|
||||
documentation](https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode).
|
||||
|
||||
## Running the fuzzers locally
|
||||
# Running the fuzzers locally
|
||||
|
||||
There is a [helper script](build.sh) to build the fuzzers, which has only been
|
||||
tested on Debian and Ubuntu linux so far. There should be no problems fuzzing on
|
||||
|
@ -34,7 +11,7 @@ mkdir build
|
|||
cd build
|
||||
export CXX=clang++
|
||||
export CXXFLAGS="-fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
|
||||
cmake .. -DFMT_SAFE_DURATION_CAST=On -DFMT_FUZZ=On -DFMT_FUZZ_LINKMAIN=Off -DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
|
||||
cmake .. -DFMT_SAFE_DURATION_CAST=On -DFMT_FUZZ=On -DFMT_FUZZ_LINKMAIN=Off -DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
|
||||
cmake --build .
|
||||
```
|
||||
should work to build the fuzzers for all platforms which clang supports.
|
||||
|
@ -44,5 +21,5 @@ Execute a fuzzer with for instance
|
|||
cd build
|
||||
export UBSAN_OPTIONS=halt_on_error=1
|
||||
mkdir out_chrono
|
||||
bin/fuzzer_chrono_duration out_chrono
|
||||
bin/fuzzer_chrono_duration out_chrono
|
||||
```
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Creates fuzzer builds of various kinds
|
||||
# - reproduce mode (no fuzzing, just enables replaying data through the fuzzers)
|
||||
# - oss-fuzz emulated mode (makes sure a simulated invocation by oss-fuzz works)
|
||||
# - libFuzzer build (you will need clang)
|
||||
# - afl build (you will need afl)
|
||||
|
@ -9,7 +8,7 @@
|
|||
#
|
||||
# Copyright (c) 2019 Paul Dreik
|
||||
#
|
||||
# License: see LICENSE.rst in the fmt root directory
|
||||
# For the license information refer to format.h.
|
||||
|
||||
set -e
|
||||
me=$(basename $0)
|
||||
|
@ -23,16 +22,7 @@ here=$(pwd)
|
|||
CXXFLAGSALL="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
|
||||
CMAKEFLAGSALL="$root -GNinja -DCMAKE_BUILD_TYPE=Debug -DFMT_DOC=Off -DFMT_TEST=Off -DFMT_FUZZ=On -DCMAKE_CXX_STANDARD=17"
|
||||
|
||||
#builds the fuzzers as one would do if using afl or just making
|
||||
#binaries for reproducing.
|
||||
builddir=$here/build-fuzzers-reproduce
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
CXX="ccache g++" CXXFLAGS="$CXXFLAGSALL" cmake \
|
||||
$CMAKEFLAGSALL
|
||||
cmake --build $builddir
|
||||
|
||||
#for performance analysis of the fuzzers
|
||||
# For performance analysis of the fuzzers.
|
||||
builddir=$here/build-fuzzers-perfanalysis
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
|
@ -43,7 +33,7 @@ $CMAKEFLAGSALL \
|
|||
|
||||
cmake --build $builddir
|
||||
|
||||
#builds the fuzzers as oss-fuzz does
|
||||
# Builds the fuzzers as oss-fuzz does.
|
||||
builddir=$here/build-fuzzers-ossfuzz
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
|
@ -56,7 +46,7 @@ cmake $CMAKEFLAGSALL \
|
|||
cmake --build $builddir
|
||||
|
||||
|
||||
#builds fuzzers for local fuzzing with libfuzzer with asan+usan
|
||||
# Builds fuzzers for local fuzzing with libfuzzer with asan+usan.
|
||||
builddir=$here/build-fuzzers-libfuzzer
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
|
@ -68,19 +58,7 @@ cmake $CMAKEFLAGSALL \
|
|||
|
||||
cmake --build $builddir
|
||||
|
||||
#builds fuzzers for local fuzzing with libfuzzer with asan only
|
||||
builddir=$here/build-fuzzers-libfuzzer-addr
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
CXX="clang++" \
|
||||
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,undefined" cmake \
|
||||
cmake $CMAKEFLAGSALL \
|
||||
-DFMT_FUZZ_LINKMAIN=Off \
|
||||
-DFMT_FUZZ_LDFLAGS="-fsanitize=fuzzer"
|
||||
|
||||
cmake --build $builddir
|
||||
|
||||
#builds a fast fuzzer for making coverage fast
|
||||
# Builds a fast fuzzer for making coverage fast.
|
||||
builddir=$here/build-fuzzers-fast
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
|
@ -94,7 +72,7 @@ cmake $CMAKEFLAGSALL \
|
|||
cmake --build $builddir
|
||||
|
||||
|
||||
#builds fuzzers for local fuzzing with afl
|
||||
# Builds fuzzers for local fuzzing with afl.
|
||||
builddir=$here/build-fuzzers-afl
|
||||
mkdir -p $builddir
|
||||
cd $builddir
|
||||
|
|
135
fmt/test/fuzzing/chrono-duration.cc
Normal file
|
@ -0,0 +1,135 @@
|
|||
// Copyright (c) 2019, Paul Dreik
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <cstdint>
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
#include "fuzzer-common.h"
|
||||
|
||||
template <typename Period, typename Rep>
|
||||
void invoke_inner(fmt::string_view format_str, Rep rep) {
|
||||
auto value = std::chrono::duration<Rep, Period>(rep);
|
||||
try {
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(format_str, value);
|
||||
#else
|
||||
fmt::memory_buffer buf;
|
||||
fmt::format_to(buf, format_str, value);
|
||||
#endif
|
||||
} catch (std::exception&) {
|
||||
}
|
||||
}
|
||||
|
||||
// Rep is a duration's representation type.
|
||||
template <typename Rep>
|
||||
void invoke_outer(const uint8_t* data, size_t size, int period) {
|
||||
// Always use a fixed location of the data.
|
||||
static_assert(sizeof(Rep) <= fixed_size, "fixed size is too small");
|
||||
if (size <= fixed_size + 1) return;
|
||||
|
||||
const Rep rep = assign_from_buf<Rep>(data);
|
||||
data += fixed_size;
|
||||
size -= fixed_size;
|
||||
|
||||
// data is already allocated separately in libFuzzer so reading past the end
|
||||
// will most likely be detected anyway.
|
||||
const auto format_str = fmt::string_view(as_chars(data), size);
|
||||
|
||||
// yocto, zepto, zetta and yotta are not handled.
|
||||
switch (period) {
|
||||
case 1:
|
||||
invoke_inner<std::atto>(format_str, rep);
|
||||
break;
|
||||
case 2:
|
||||
invoke_inner<std::femto>(format_str, rep);
|
||||
break;
|
||||
case 3:
|
||||
invoke_inner<std::pico>(format_str, rep);
|
||||
break;
|
||||
case 4:
|
||||
invoke_inner<std::nano>(format_str, rep);
|
||||
break;
|
||||
case 5:
|
||||
invoke_inner<std::micro>(format_str, rep);
|
||||
break;
|
||||
case 6:
|
||||
invoke_inner<std::milli>(format_str, rep);
|
||||
break;
|
||||
case 7:
|
||||
invoke_inner<std::centi>(format_str, rep);
|
||||
break;
|
||||
case 8:
|
||||
invoke_inner<std::deci>(format_str, rep);
|
||||
break;
|
||||
case 9:
|
||||
invoke_inner<std::deca>(format_str, rep);
|
||||
break;
|
||||
case 10:
|
||||
invoke_inner<std::kilo>(format_str, rep);
|
||||
break;
|
||||
case 11:
|
||||
invoke_inner<std::mega>(format_str, rep);
|
||||
break;
|
||||
case 12:
|
||||
invoke_inner<std::giga>(format_str, rep);
|
||||
break;
|
||||
case 13:
|
||||
invoke_inner<std::tera>(format_str, rep);
|
||||
break;
|
||||
case 14:
|
||||
invoke_inner<std::peta>(format_str, rep);
|
||||
break;
|
||||
case 15:
|
||||
invoke_inner<std::exa>(format_str, rep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
if (size <= 4) return 0;
|
||||
|
||||
const auto representation = data[0];
|
||||
const auto period = data[1];
|
||||
data += 2;
|
||||
size -= 2;
|
||||
|
||||
switch (representation) {
|
||||
case 1:
|
||||
invoke_outer<char>(data, size, period);
|
||||
break;
|
||||
case 2:
|
||||
invoke_outer<signed char>(data, size, period);
|
||||
break;
|
||||
case 3:
|
||||
invoke_outer<unsigned char>(data, size, period);
|
||||
break;
|
||||
case 4:
|
||||
invoke_outer<short>(data, size, period);
|
||||
break;
|
||||
case 5:
|
||||
invoke_outer<unsigned short>(data, size, period);
|
||||
break;
|
||||
case 6:
|
||||
invoke_outer<int>(data, size, period);
|
||||
break;
|
||||
case 7:
|
||||
invoke_outer<unsigned int>(data, size, period);
|
||||
break;
|
||||
case 8:
|
||||
invoke_outer<long>(data, size, period);
|
||||
break;
|
||||
case 9:
|
||||
invoke_outer<unsigned long>(data, size, period);
|
||||
break;
|
||||
case 10:
|
||||
invoke_outer<float>(data, size, period);
|
||||
break;
|
||||
case 11:
|
||||
invoke_outer<double>(data, size, period);
|
||||
break;
|
||||
case 12:
|
||||
invoke_outer<long double>(data, size, period);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,152 +0,0 @@
|
|||
// Copyright (c) 2019, Paul Dreik
|
||||
// License: see LICENSE.rst in the fmt root directory
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "fuzzer_common.h"
|
||||
|
||||
template <typename Item, typename Ratio>
|
||||
void invoke_inner(fmt::string_view formatstring, const Item item) {
|
||||
const std::chrono::duration<Item, Ratio> value(item);
|
||||
try {
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(formatstring, value);
|
||||
#else
|
||||
fmt::memory_buffer buf;
|
||||
fmt::format_to(buf, formatstring, value);
|
||||
#endif
|
||||
} catch (std::exception& /*e*/) {
|
||||
}
|
||||
}
|
||||
|
||||
// Item is the underlying type for duration (int, long etc)
|
||||
template <typename Item>
|
||||
void invoke_outer(const uint8_t* Data, size_t Size, const int scaling) {
|
||||
// always use a fixed location of the data
|
||||
using fmt_fuzzer::Nfixed;
|
||||
|
||||
constexpr auto N = sizeof(Item);
|
||||
static_assert(N <= Nfixed, "fixed size is too small");
|
||||
if (Size <= Nfixed + 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Item item = fmt_fuzzer::assignFromBuf<Item>(Data);
|
||||
|
||||
// fast forward
|
||||
Data += Nfixed;
|
||||
Size -= Nfixed;
|
||||
|
||||
// Data is already allocated separately in libFuzzer so reading past
|
||||
// the end will most likely be detected anyway
|
||||
const auto formatstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
||||
|
||||
// doit_impl<Item,std::yocto>(buf.data(),item);
|
||||
// doit_impl<Item,std::zepto>(buf.data(),item);
|
||||
switch (scaling) {
|
||||
case 1:
|
||||
invoke_inner<Item, std::atto>(formatstring, item);
|
||||
break;
|
||||
case 2:
|
||||
invoke_inner<Item, std::femto>(formatstring, item);
|
||||
break;
|
||||
case 3:
|
||||
invoke_inner<Item, std::pico>(formatstring, item);
|
||||
break;
|
||||
case 4:
|
||||
invoke_inner<Item, std::nano>(formatstring, item);
|
||||
break;
|
||||
case 5:
|
||||
invoke_inner<Item, std::micro>(formatstring, item);
|
||||
break;
|
||||
case 6:
|
||||
invoke_inner<Item, std::milli>(formatstring, item);
|
||||
break;
|
||||
case 7:
|
||||
invoke_inner<Item, std::centi>(formatstring, item);
|
||||
break;
|
||||
case 8:
|
||||
invoke_inner<Item, std::deci>(formatstring, item);
|
||||
break;
|
||||
case 9:
|
||||
invoke_inner<Item, std::deca>(formatstring, item);
|
||||
break;
|
||||
case 10:
|
||||
invoke_inner<Item, std::kilo>(formatstring, item);
|
||||
break;
|
||||
case 11:
|
||||
invoke_inner<Item, std::mega>(formatstring, item);
|
||||
break;
|
||||
case 12:
|
||||
invoke_inner<Item, std::giga>(formatstring, item);
|
||||
break;
|
||||
case 13:
|
||||
invoke_inner<Item, std::tera>(formatstring, item);
|
||||
break;
|
||||
case 14:
|
||||
invoke_inner<Item, std::peta>(formatstring, item);
|
||||
break;
|
||||
case 15:
|
||||
invoke_inner<Item, std::exa>(formatstring, item);
|
||||
}
|
||||
// doit_impl<Item,std::zeta>(buf.data(),item);
|
||||
// doit_impl<Item,std::yotta>(buf.data(),item);
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
|
||||
if (Size <= 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto representation = Data[0];
|
||||
const auto scaling = Data[1];
|
||||
Data += 2;
|
||||
Size -= 2;
|
||||
|
||||
switch (representation) {
|
||||
case 1:
|
||||
invoke_outer<char>(Data, Size, scaling);
|
||||
break;
|
||||
case 2:
|
||||
invoke_outer<unsigned char>(Data, Size, scaling);
|
||||
break;
|
||||
case 3:
|
||||
invoke_outer<signed char>(Data, Size, scaling);
|
||||
break;
|
||||
case 4:
|
||||
invoke_outer<short>(Data, Size, scaling);
|
||||
break;
|
||||
case 5:
|
||||
invoke_outer<unsigned short>(Data, Size, scaling);
|
||||
break;
|
||||
case 6:
|
||||
invoke_outer<int>(Data, Size, scaling);
|
||||
break;
|
||||
case 7:
|
||||
invoke_outer<unsigned int>(Data, Size, scaling);
|
||||
break;
|
||||
case 8:
|
||||
invoke_outer<long>(Data, Size, scaling);
|
||||
break;
|
||||
case 9:
|
||||
invoke_outer<unsigned long>(Data, Size, scaling);
|
||||
break;
|
||||
case 10:
|
||||
invoke_outer<float>(Data, Size, scaling);
|
||||
break;
|
||||
case 11:
|
||||
invoke_outer<double>(Data, Size, scaling);
|
||||
break;
|
||||
case 12:
|
||||
invoke_outer<long double>(Data, Size, scaling);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
34
fmt/test/fuzzing/float.cc
Normal file
|
@ -0,0 +1,34 @@
|
|||
// A fuzzer for floating-point formatter.
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <limits>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "fuzzer-common.h"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
if (size <= sizeof(double) || !std::numeric_limits<double>::is_iec559)
|
||||
return 0;
|
||||
|
||||
auto value = assign_from_buf<double>(data);
|
||||
auto buffer = fmt::memory_buffer();
|
||||
fmt::format_to(buffer, "{}", value);
|
||||
|
||||
// Check a round trip.
|
||||
if (std::isnan(value)) {
|
||||
auto nan = std::signbit(value) ? "-nan" : "nan";
|
||||
if (fmt::string_view(buffer.data(), buffer.size()) != nan)
|
||||
throw std::runtime_error("round trip failure");
|
||||
return 0;
|
||||
}
|
||||
buffer.push_back('\0');
|
||||
char* ptr = nullptr;
|
||||
if (std::strtod(buffer.data(), &ptr) != value)
|
||||
throw std::runtime_error("round trip failure");
|
||||
if (ptr + 1 != buffer.end())
|
||||
throw std::runtime_error("unparsed output");
|
||||
return 0;
|
||||
}
|
75
fmt/test/fuzzing/fuzzer-common.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) 2019, Paul Dreik
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FUZZER_COMMON_H
|
||||
#define FUZZER_COMMON_H
|
||||
|
||||
#include <cstdint> // std::uint8_t
|
||||
#include <cstring> // memcpy
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/core.h>
|
||||
|
||||
// One can format to either a string, or a buffer. The latter is faster, but
|
||||
// one may be interested in formatting to a string instead to verify it works
|
||||
// as intended. To avoid a combinatoric explosion, select this at compile time
|
||||
// instead of dynamically from the fuzz data.
|
||||
#define FMT_FUZZ_FORMAT_TO_STRING 0
|
||||
|
||||
// If {fmt} is given a buffer that is separately allocated, chances that address
|
||||
// sanitizer detects out of bound reads is much higher. However, it slows down
|
||||
// the fuzzing.
|
||||
#define FMT_FUZZ_SEPARATE_ALLOCATION 1
|
||||
|
||||
// The size of the largest possible type in use.
|
||||
// To let the the fuzzer mutation be efficient at cross pollinating between
|
||||
// different types, use a fixed size format. The same bit pattern, interpreted
|
||||
// as another type, is likely interesting.
|
||||
constexpr auto fixed_size = 16;
|
||||
|
||||
// Casts data to a char pointer.
|
||||
template <typename T> inline const char* as_chars(const T* data) {
|
||||
return reinterpret_cast<const char*>(data);
|
||||
}
|
||||
|
||||
// Casts data to a byte pointer.
|
||||
template <typename T> inline const std::uint8_t* as_bytes(const T* data) {
|
||||
return reinterpret_cast<const std::uint8_t*>(data);
|
||||
}
|
||||
|
||||
// Blits bytes from data to form an (assumed trivially constructible) object
|
||||
// of type Item.
|
||||
template <class Item> inline Item assign_from_buf(const std::uint8_t* data) {
|
||||
auto item = Item();
|
||||
std::memcpy(&item, data, sizeof(Item));
|
||||
return item;
|
||||
}
|
||||
|
||||
// Reads a boolean value by looking at the first byte from data.
|
||||
template <> inline bool assign_from_buf<bool>(const std::uint8_t* data) {
|
||||
return *data != 0;
|
||||
}
|
||||
|
||||
struct data_to_string {
|
||||
#if FMT_FUZZ_SEPARATE_ALLOCATION
|
||||
std::vector<char> buffer;
|
||||
|
||||
data_to_string(const uint8_t* data, size_t size, bool add_terminator = false)
|
||||
: buffer(size + (add_terminator ? 1 : 0)) {
|
||||
std::memcpy(buffer.data(), data, size);
|
||||
}
|
||||
|
||||
fmt::string_view get() const { return {buffer.data(), buffer.size()}; }
|
||||
#else
|
||||
fmt::string_view sv;
|
||||
|
||||
data_to_string(const uint8_t* data, size_t size, bool = false)
|
||||
: str(as_chars(data), size) {}
|
||||
|
||||
fmt::string_view get() const { return sv; }
|
||||
#endif
|
||||
|
||||
const char* data() const { return get().data(); }
|
||||
};
|
||||
|
||||
#endif // FUZZER_COMMON_H
|
|
@ -1,67 +0,0 @@
|
|||
#ifndef FUZZER_COMMON_H
|
||||
#define FUZZER_COMMON_H
|
||||
|
||||
// Copyright (c) 2019, Paul Dreik
|
||||
// License: see LICENSE.rst in the fmt root directory
|
||||
|
||||
#include <cstdint> // std::uint8_t
|
||||
#include <cstring> // memcpy
|
||||
#include <type_traits> // trivially copyable
|
||||
|
||||
// one can format to either a string, or a buf. buf is faster,
|
||||
// but one may be interested in formatting to a string instead to
|
||||
// verify it works as intended. to avoid a combinatoric explosion,
|
||||
// select this at compile time instead of dynamically from the fuzz data
|
||||
#define FMT_FUZZ_FORMAT_TO_STRING 0
|
||||
|
||||
// if fmt is given a buffer that is separately allocated,
|
||||
// chances that address sanitizer detects out of bound reads is
|
||||
// much higher. However, it slows down the fuzzing.
|
||||
#define FMT_FUZZ_SEPARATE_ALLOCATION 1
|
||||
|
||||
// To let the the fuzzer mutation be efficient at cross pollinating
|
||||
// between different types, use a fixed size format.
|
||||
// The same bit pattern, interpreted as another type,
|
||||
// is likely interesting.
|
||||
// For this, we must know the size of the largest possible type in use.
|
||||
|
||||
// There are some problems on travis, claiming Nfixed is not a constant
|
||||
// expression which seems to be an issue with older versions of libstdc++
|
||||
#if _GLIBCXX_RELEASE >= 7
|
||||
# include <algorithm>
|
||||
namespace fmt_fuzzer {
|
||||
constexpr auto Nfixed = std::max(sizeof(long double), sizeof(std::intmax_t));
|
||||
}
|
||||
#else
|
||||
namespace fmt_fuzzer {
|
||||
constexpr auto Nfixed = 16;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace fmt_fuzzer {
|
||||
// view data as a c char pointer.
|
||||
template <typename T> inline const char* as_chars(const T* data) {
|
||||
return static_cast<const char*>(static_cast<const void*>(data));
|
||||
}
|
||||
|
||||
// view data as a byte pointer
|
||||
template <typename T> inline const std::uint8_t* as_bytes(const T* data) {
|
||||
return static_cast<const std::uint8_t*>(static_cast<const void*>(data));
|
||||
}
|
||||
|
||||
// blits bytes from Data to form an (assumed trivially constructible) object
|
||||
// of type Item
|
||||
template <class Item> inline Item assignFromBuf(const std::uint8_t* Data) {
|
||||
Item item{};
|
||||
std::memcpy(&item, Data, sizeof(Item));
|
||||
return item;
|
||||
}
|
||||
|
||||
// reads a boolean value by looking at the first byte from Data
|
||||
template <> inline bool assignFromBuf<bool>(const std::uint8_t* Data) {
|
||||
return !!Data[0];
|
||||
}
|
||||
|
||||
} // namespace fmt_fuzzer
|
||||
|
||||
#endif // FUZZER_COMMON_H
|
22
fmt/test/fuzzing/main.cc
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "fuzzer-common.h"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size);
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::ifstream in(argv[i]);
|
||||
assert(in);
|
||||
in.seekg(0, std::ios_base::end);
|
||||
const auto size = in.tellg();
|
||||
assert(size >= 0);
|
||||
in.seekg(0, std::ios_base::beg);
|
||||
std::vector<char> buf(static_cast<size_t>(size));
|
||||
in.read(buf.data(), size);
|
||||
assert(in.gcount() == size);
|
||||
LLVMFuzzerTestOneInput(as_bytes(buf.data()), buf.size());
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include "fuzzer_common.h"
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size);
|
||||
int main(int argc, char* argv[]) {
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::ifstream in(argv[i]);
|
||||
assert(in);
|
||||
in.seekg(0, std::ios_base::end);
|
||||
const auto pos = in.tellg();
|
||||
assert(pos >= 0);
|
||||
in.seekg(0, std::ios_base::beg);
|
||||
std::vector<char> buf(static_cast<size_t>(pos));
|
||||
in.read(buf.data(), static_cast<long>(buf.size()));
|
||||
assert(in.gcount() == pos);
|
||||
LLVMFuzzerTestOneInput(fmt_fuzzer::as_bytes(buf.data()), buf.size());
|
||||
}
|
||||
}
|
100
fmt/test/fuzzing/named-arg.cc
Normal file
|
@ -0,0 +1,100 @@
|
|||
// Copyright (c) 2019, Paul Dreik
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
#include "fuzzer-common.h"
|
||||
|
||||
template <typename T>
|
||||
void invoke_fmt(const uint8_t* data, size_t size, unsigned arg_name_size) {
|
||||
static_assert(sizeof(T) <= fixed_size, "fixed_size too small");
|
||||
if (size <= fixed_size) return;
|
||||
const T value = assign_from_buf<T>(data);
|
||||
data += fixed_size;
|
||||
size -= fixed_size;
|
||||
|
||||
if (arg_name_size <= 0 || arg_name_size >= size) return;
|
||||
data_to_string arg_name(data, arg_name_size, true);
|
||||
data += arg_name_size;
|
||||
size -= arg_name_size;
|
||||
|
||||
data_to_string format_str(data, size);
|
||||
try {
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message =
|
||||
fmt::format(format_str.get(), fmt::arg(arg_name.data(), value));
|
||||
#else
|
||||
fmt::memory_buffer out;
|
||||
fmt::format_to(out, format_str.get(), fmt::arg(arg_name.data(), value));
|
||||
#endif
|
||||
} catch (std::exception&) {
|
||||
}
|
||||
}
|
||||
|
||||
// For dynamic dispatching to an explicit instantiation.
|
||||
template <typename Callback> void invoke(int type, Callback callback) {
|
||||
switch (type) {
|
||||
case 0:
|
||||
callback(bool());
|
||||
break;
|
||||
case 1:
|
||||
callback(char());
|
||||
break;
|
||||
case 2:
|
||||
using sc = signed char;
|
||||
callback(sc());
|
||||
break;
|
||||
case 3:
|
||||
using uc = unsigned char;
|
||||
callback(uc());
|
||||
break;
|
||||
case 4:
|
||||
callback(short());
|
||||
break;
|
||||
case 5:
|
||||
using us = unsigned short;
|
||||
callback(us());
|
||||
break;
|
||||
case 6:
|
||||
callback(int());
|
||||
break;
|
||||
case 7:
|
||||
callback(unsigned());
|
||||
break;
|
||||
case 8:
|
||||
callback(long());
|
||||
break;
|
||||
case 9:
|
||||
using ul = unsigned long;
|
||||
callback(ul());
|
||||
break;
|
||||
case 10:
|
||||
callback(float());
|
||||
break;
|
||||
case 11:
|
||||
callback(double());
|
||||
break;
|
||||
case 12:
|
||||
using LD = long double;
|
||||
callback(LD());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
if (size <= 3) return 0;
|
||||
|
||||
// Switch types depending on the first byte of the input.
|
||||
const auto type = data[0] & 0x0F;
|
||||
const unsigned arg_name_size = (data[0] & 0xF0) >> 4;
|
||||
data++;
|
||||
size--;
|
||||
|
||||
invoke(type, [=](auto arg) {
|
||||
invoke_fmt<decltype(arg)>(data, size, arg_name_size);
|
||||
});
|
||||
return 0;
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
// Copyright (c) 2019, Paul Dreik
|
||||
// License: see LICENSE.rst in the fmt root directory
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/core.h>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include "fuzzer_common.h"
|
||||
|
||||
template <typename Item1>
|
||||
void invoke_fmt(const uint8_t* Data, size_t Size, unsigned int argsize) {
|
||||
constexpr auto N1 = sizeof(Item1);
|
||||
static_assert(N1 <= fmt_fuzzer::Nfixed, "Nfixed too small");
|
||||
if (Size <= fmt_fuzzer::Nfixed) {
|
||||
return;
|
||||
}
|
||||
const Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
|
||||
|
||||
Data += fmt_fuzzer::Nfixed;
|
||||
Size -= fmt_fuzzer::Nfixed;
|
||||
|
||||
// how many chars should be used for the argument name?
|
||||
if (argsize <= 0 || argsize >= Size) {
|
||||
return;
|
||||
}
|
||||
|
||||
// allocating buffers separately is slower, but increases chances
|
||||
// of detecting memory errors
|
||||
#if FMT_FUZZ_SEPARATE_ALLOCATION
|
||||
std::vector<char> argnamebuffer(argsize + 1);
|
||||
std::memcpy(argnamebuffer.data(), Data, argsize);
|
||||
auto argname = argnamebuffer.data();
|
||||
#else
|
||||
auto argname = fmt_fuzzer::as_chars(Data);
|
||||
#endif
|
||||
Data += argsize;
|
||||
Size -= argsize;
|
||||
|
||||
#if FMT_FUZZ_SEPARATE_ALLOCATION
|
||||
// allocates as tight as possible, making it easier to catch buffer overruns.
|
||||
std::vector<char> fmtstringbuffer(Size);
|
||||
std::memcpy(fmtstringbuffer.data(), Data, Size);
|
||||
auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
|
||||
#else
|
||||
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
||||
#endif
|
||||
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(fmtstring, fmt::arg(argname, item1));
|
||||
#else
|
||||
fmt::memory_buffer outbuf;
|
||||
fmt::format_to(outbuf, fmtstring, fmt::arg(argname, item1));
|
||||
#endif
|
||||
}
|
||||
|
||||
// for dynamic dispatching to an explicit instantiation
|
||||
template <typename Callback> void invoke(int index, Callback callback) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
callback(bool{});
|
||||
break;
|
||||
case 1:
|
||||
callback(char{});
|
||||
break;
|
||||
case 2:
|
||||
using sc = signed char;
|
||||
callback(sc{});
|
||||
break;
|
||||
case 3:
|
||||
using uc = unsigned char;
|
||||
callback(uc{});
|
||||
break;
|
||||
case 4:
|
||||
callback(short{});
|
||||
break;
|
||||
case 5:
|
||||
using us = unsigned short;
|
||||
callback(us{});
|
||||
break;
|
||||
case 6:
|
||||
callback(int{});
|
||||
break;
|
||||
case 7:
|
||||
callback(unsigned{});
|
||||
break;
|
||||
case 8:
|
||||
callback(long{});
|
||||
break;
|
||||
case 9:
|
||||
using ul = unsigned long;
|
||||
callback(ul{});
|
||||
break;
|
||||
case 10:
|
||||
callback(float{});
|
||||
break;
|
||||
case 11:
|
||||
callback(double{});
|
||||
break;
|
||||
case 12:
|
||||
using LD = long double;
|
||||
callback(LD{});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
|
||||
if (Size <= 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// switch types depending on the first byte of the input
|
||||
const auto first = Data[0] & 0x0F;
|
||||
const unsigned int second = (Data[0] & 0xF0) >> 4;
|
||||
Data++;
|
||||
Size--;
|
||||
|
||||
auto outerfcn = [=](auto param1) {
|
||||
invoke_fmt<decltype(param1)>(Data, Size, second);
|
||||
};
|
||||
|
||||
try {
|
||||
invoke(first, outerfcn);
|
||||
} catch (std::exception& /*e*/) {
|
||||
}
|
||||
return 0;
|
||||
}
|
91
fmt/test/fuzzing/one-arg.cc
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Copyright (c) 2019, Paul Dreik
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
#include "fuzzer-common.h"
|
||||
|
||||
template <typename T, typename Repr>
|
||||
const T* from_repr(const Repr& r) { return &r; }
|
||||
|
||||
template <>
|
||||
const std::tm* from_repr<std::tm>(const std::time_t& t) {
|
||||
return std::localtime(&t);
|
||||
}
|
||||
|
||||
template <typename T, typename Repr = T>
|
||||
void invoke_fmt(const uint8_t* data, size_t size) {
|
||||
static_assert(sizeof(Repr) <= fixed_size, "Nfixed is too small");
|
||||
if (size <= fixed_size) return;
|
||||
auto repr = assign_from_buf<Repr>(data);
|
||||
const T* value = from_repr<T>(repr);
|
||||
if (!value) return;
|
||||
data += fixed_size;
|
||||
size -= fixed_size;
|
||||
data_to_string format_str(data, size);
|
||||
try {
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(format_str.get(), *value);
|
||||
#else
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_to(message, format_str.get(), *value);
|
||||
#endif
|
||||
} catch (std::exception&) {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
if (size <= 3) return 0;
|
||||
|
||||
const auto first = data[0];
|
||||
data++;
|
||||
size--;
|
||||
|
||||
switch (first) {
|
||||
case 0:
|
||||
invoke_fmt<bool>(data, size);
|
||||
break;
|
||||
case 1:
|
||||
invoke_fmt<char>(data, size);
|
||||
break;
|
||||
case 2:
|
||||
invoke_fmt<unsigned char>(data, size);
|
||||
break;
|
||||
case 3:
|
||||
invoke_fmt<signed char>(data, size);
|
||||
break;
|
||||
case 4:
|
||||
invoke_fmt<short>(data, size);
|
||||
break;
|
||||
case 5:
|
||||
invoke_fmt<unsigned short>(data, size);
|
||||
break;
|
||||
case 6:
|
||||
invoke_fmt<int>(data, size);
|
||||
break;
|
||||
case 7:
|
||||
invoke_fmt<unsigned int>(data, size);
|
||||
break;
|
||||
case 8:
|
||||
invoke_fmt<long>(data, size);
|
||||
break;
|
||||
case 9:
|
||||
invoke_fmt<unsigned long>(data, size);
|
||||
break;
|
||||
case 10:
|
||||
invoke_fmt<float>(data, size);
|
||||
break;
|
||||
case 11:
|
||||
invoke_fmt<double>(data, size);
|
||||
break;
|
||||
case 12:
|
||||
invoke_fmt<long double>(data, size);
|
||||
break;
|
||||
case 13:
|
||||
invoke_fmt<std::tm, std::time_t>(data, size);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
// Copyright (c) 2019, Paul Dreik
|
||||
// License: see LICENSE.rst in the fmt root directory
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
#include "fuzzer_common.h"
|
||||
|
||||
using fmt_fuzzer::Nfixed;
|
||||
|
||||
template <typename Item>
|
||||
void invoke_fmt(const uint8_t* Data, size_t Size) {
|
||||
constexpr auto N = sizeof(Item);
|
||||
static_assert(N <= Nfixed, "Nfixed is too small");
|
||||
if (Size <= Nfixed) {
|
||||
return;
|
||||
}
|
||||
const Item item = fmt_fuzzer::assignFromBuf<Item>(Data);
|
||||
Data += Nfixed;
|
||||
Size -= Nfixed;
|
||||
|
||||
#if FMT_FUZZ_SEPARATE_ALLOCATION
|
||||
// allocates as tight as possible, making it easier to catch buffer overruns.
|
||||
std::vector<char> fmtstringbuffer(Size);
|
||||
std::memcpy(fmtstringbuffer.data(), Data, Size);
|
||||
auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
|
||||
#else
|
||||
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
||||
#endif
|
||||
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(fmtstring, item);
|
||||
#else
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_to(message, fmtstring, item);
|
||||
#endif
|
||||
}
|
||||
|
||||
void invoke_fmt_time(const uint8_t* Data, size_t Size) {
|
||||
using Item = std::time_t;
|
||||
constexpr auto N = sizeof(Item);
|
||||
static_assert(N <= Nfixed, "Nfixed too small");
|
||||
if (Size <= Nfixed) {
|
||||
return;
|
||||
}
|
||||
const Item item = fmt_fuzzer::assignFromBuf<Item>(Data);
|
||||
Data += Nfixed;
|
||||
Size -= Nfixed;
|
||||
#if FMT_FUZZ_SEPARATE_ALLOCATION
|
||||
// allocates as tight as possible, making it easier to catch buffer overruns.
|
||||
std::vector<char> fmtstringbuffer(Size);
|
||||
std::memcpy(fmtstringbuffer.data(), Data, Size);
|
||||
auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
|
||||
#else
|
||||
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
||||
#endif
|
||||
auto* b = std::localtime(&item);
|
||||
if (b) {
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(fmtstring, *b);
|
||||
#else
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_to(message, fmtstring, *b);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
|
||||
if (Size <= 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto first = Data[0];
|
||||
Data++;
|
||||
Size--;
|
||||
|
||||
try {
|
||||
switch (first) {
|
||||
case 0:
|
||||
invoke_fmt<bool>(Data, Size);
|
||||
break;
|
||||
case 1:
|
||||
invoke_fmt<char>(Data, Size);
|
||||
break;
|
||||
case 2:
|
||||
invoke_fmt<unsigned char>(Data, Size);
|
||||
break;
|
||||
case 3:
|
||||
invoke_fmt<signed char>(Data, Size);
|
||||
break;
|
||||
case 4:
|
||||
invoke_fmt<short>(Data, Size);
|
||||
break;
|
||||
case 5:
|
||||
invoke_fmt<unsigned short>(Data, Size);
|
||||
break;
|
||||
case 6:
|
||||
invoke_fmt<int>(Data, Size);
|
||||
break;
|
||||
case 7:
|
||||
invoke_fmt<unsigned int>(Data, Size);
|
||||
break;
|
||||
case 8:
|
||||
invoke_fmt<long>(Data, Size);
|
||||
break;
|
||||
case 9:
|
||||
invoke_fmt<unsigned long>(Data, Size);
|
||||
break;
|
||||
case 10:
|
||||
invoke_fmt<float>(Data, Size);
|
||||
break;
|
||||
case 11:
|
||||
invoke_fmt<double>(Data, Size);
|
||||
break;
|
||||
case 12:
|
||||
invoke_fmt<long double>(Data, Size);
|
||||
break;
|
||||
case 13:
|
||||
invoke_fmt_time(Data, Size);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (std::exception& /*e*/) {
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
// Copyright (c) 2019, Paul Dreik
|
||||
// License: see LICENSE.rst in the fmt root directory
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/printf.h>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "fuzzer_common.h"
|
||||
|
||||
using fmt_fuzzer::Nfixed;
|
||||
|
||||
template <typename Item1, typename Item2>
|
||||
void invoke_fmt(const uint8_t* Data, size_t Size) {
|
||||
constexpr auto N1 = sizeof(Item1);
|
||||
constexpr auto N2 = sizeof(Item2);
|
||||
static_assert(N1 <= Nfixed, "size1 exceeded");
|
||||
static_assert(N2 <= Nfixed, "size2 exceeded");
|
||||
if (Size <= Nfixed + Nfixed) {
|
||||
return;
|
||||
}
|
||||
Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
|
||||
Data += Nfixed;
|
||||
Size -= Nfixed;
|
||||
|
||||
Item2 item2 = fmt_fuzzer::assignFromBuf<Item2>(Data);
|
||||
Data += Nfixed;
|
||||
Size -= Nfixed;
|
||||
|
||||
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
||||
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(fmtstring, item1, item2);
|
||||
#else
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_to(message, fmtstring, item1, item2);
|
||||
#endif
|
||||
}
|
||||
|
||||
// for dynamic dispatching to an explicit instantiation
|
||||
template <typename Callback> void invoke(int index, Callback callback) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
callback(bool{});
|
||||
break;
|
||||
case 1:
|
||||
callback(char{});
|
||||
break;
|
||||
case 2:
|
||||
using sc = signed char;
|
||||
callback(sc{});
|
||||
break;
|
||||
case 3:
|
||||
using uc = unsigned char;
|
||||
callback(uc{});
|
||||
break;
|
||||
case 4:
|
||||
callback(short{});
|
||||
break;
|
||||
case 5:
|
||||
using us = unsigned short;
|
||||
callback(us{});
|
||||
break;
|
||||
case 6:
|
||||
callback(int{});
|
||||
break;
|
||||
case 7:
|
||||
callback(unsigned{});
|
||||
break;
|
||||
case 8:
|
||||
callback(long{});
|
||||
break;
|
||||
case 9:
|
||||
using ul = unsigned long;
|
||||
callback(ul{});
|
||||
break;
|
||||
case 10:
|
||||
callback(float{});
|
||||
break;
|
||||
case 11:
|
||||
callback(double{});
|
||||
break;
|
||||
case 12:
|
||||
using LD = long double;
|
||||
callback(LD{});
|
||||
break;
|
||||
case 13:
|
||||
using ptr = void*;
|
||||
callback(ptr{});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
|
||||
if (Size <= 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// switch types depending on the first byte of the input
|
||||
const auto first = Data[0] & 0x0F;
|
||||
const auto second = (Data[0] & 0xF0) >> 4;
|
||||
Data++;
|
||||
Size--;
|
||||
|
||||
auto outer = [=](auto param1) {
|
||||
auto inner = [=](auto param2) {
|
||||
invoke_fmt<decltype(param1), decltype(param2)>(Data, Size);
|
||||
};
|
||||
invoke(second, inner);
|
||||
};
|
||||
|
||||
try {
|
||||
invoke(first, outer);
|
||||
} catch (std::exception& /*e*/) {
|
||||
}
|
||||
return 0;
|
||||
}
|
105
fmt/test/fuzzing/two-args.cc
Normal file
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) 2019, Paul Dreik
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "fuzzer-common.h"
|
||||
|
||||
template <typename Item1, typename Item2>
|
||||
void invoke_fmt(const uint8_t* data, size_t size) {
|
||||
static_assert(sizeof(Item1) <= fixed_size, "size1 exceeded");
|
||||
static_assert(sizeof(Item2) <= fixed_size, "size2 exceeded");
|
||||
if (size <= fixed_size + fixed_size) return;
|
||||
|
||||
const Item1 item1 = assign_from_buf<Item1>(data);
|
||||
data += fixed_size;
|
||||
size -= fixed_size;
|
||||
|
||||
const Item2 item2 = assign_from_buf<Item2>(data);
|
||||
data += fixed_size;
|
||||
size -= fixed_size;
|
||||
|
||||
auto format_str = fmt::string_view(as_chars(data), size);
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(format_str, item1, item2);
|
||||
#else
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_to(message, format_str, item1, item2);
|
||||
#endif
|
||||
}
|
||||
|
||||
// For dynamic dispatching to an explicit instantiation.
|
||||
template <typename Callback> void invoke(int index, Callback callback) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
callback(bool());
|
||||
break;
|
||||
case 1:
|
||||
callback(char());
|
||||
break;
|
||||
case 2:
|
||||
using sc = signed char;
|
||||
callback(sc());
|
||||
break;
|
||||
case 3:
|
||||
using uc = unsigned char;
|
||||
callback(uc());
|
||||
break;
|
||||
case 4:
|
||||
callback(short());
|
||||
break;
|
||||
case 5:
|
||||
using us = unsigned short;
|
||||
callback(us());
|
||||
break;
|
||||
case 6:
|
||||
callback(int());
|
||||
break;
|
||||
case 7:
|
||||
callback(unsigned());
|
||||
break;
|
||||
case 8:
|
||||
callback(long());
|
||||
break;
|
||||
case 9:
|
||||
using ul = unsigned long;
|
||||
callback(ul());
|
||||
break;
|
||||
case 10:
|
||||
callback(float());
|
||||
break;
|
||||
case 11:
|
||||
callback(double());
|
||||
break;
|
||||
case 12:
|
||||
using LD = long double;
|
||||
callback(LD());
|
||||
break;
|
||||
case 13:
|
||||
using ptr = void*;
|
||||
callback(ptr());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
||||
if (size <= 3) return 0;
|
||||
|
||||
// Switch types depending on the first byte of the input.
|
||||
const auto type1 = data[0] & 0x0F;
|
||||
const auto type2 = (data[0] & 0xF0) >> 4;
|
||||
data++;
|
||||
size--;
|
||||
try {
|
||||
invoke(type1, [=](auto param1) {
|
||||
invoke(type2, [=](auto param2) {
|
||||
invoke_fmt<decltype(param1), decltype(param2)>(data, size);
|
||||
});
|
||||
});
|
||||
} catch (std::exception&) {
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
// Copyright (c) 2019, Paul Dreik
|
||||
// License: see LICENSE.rst in the fmt root directory
|
||||
#include <fmt/format.h>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include "fuzzer_common.h"
|
||||
|
||||
constexpr auto Nfixed = fmt_fuzzer::Nfixed;
|
||||
|
||||
template <typename Item1, typename Item2>
|
||||
void invoke_fmt(const uint8_t* Data, size_t Size) {
|
||||
constexpr auto N1 = sizeof(Item1);
|
||||
constexpr auto N2 = sizeof(Item2);
|
||||
static_assert(N1 <= Nfixed, "size1 exceeded");
|
||||
static_assert(N2 <= Nfixed, "size2 exceeded");
|
||||
if (Size <= Nfixed + Nfixed) {
|
||||
return;
|
||||
}
|
||||
const Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
|
||||
Data += Nfixed;
|
||||
Size -= Nfixed;
|
||||
|
||||
const Item2 item2 = fmt_fuzzer::assignFromBuf<Item2>(Data);
|
||||
Data += Nfixed;
|
||||
Size -= Nfixed;
|
||||
|
||||
auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
|
||||
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(fmtstring, item1, item2);
|
||||
#else
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_to(message, fmtstring, item1, item2);
|
||||
#endif
|
||||
}
|
||||
|
||||
// for dynamic dispatching to an explicit instantiation
|
||||
template <typename Callback> void invoke(int index, Callback callback) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
callback(bool{});
|
||||
break;
|
||||
case 1:
|
||||
callback(char{});
|
||||
break;
|
||||
case 2:
|
||||
using sc = signed char;
|
||||
callback(sc{});
|
||||
break;
|
||||
case 3:
|
||||
using uc = unsigned char;
|
||||
callback(uc{});
|
||||
break;
|
||||
case 4:
|
||||
callback(short{});
|
||||
break;
|
||||
case 5:
|
||||
using us = unsigned short;
|
||||
callback(us{});
|
||||
break;
|
||||
case 6:
|
||||
callback(int{});
|
||||
break;
|
||||
case 7:
|
||||
callback(unsigned{});
|
||||
break;
|
||||
case 8:
|
||||
callback(long{});
|
||||
break;
|
||||
case 9:
|
||||
using ul = unsigned long;
|
||||
callback(ul{});
|
||||
break;
|
||||
case 10:
|
||||
callback(float{});
|
||||
break;
|
||||
case 11:
|
||||
callback(double{});
|
||||
break;
|
||||
case 12:
|
||||
using LD = long double;
|
||||
callback(LD{});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
|
||||
if (Size <= 3) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// switch types depending on the first byte of the input
|
||||
const auto first = Data[0] & 0x0F;
|
||||
const auto second = (Data[0] & 0xF0) >> 4;
|
||||
Data++;
|
||||
Size--;
|
||||
|
||||
auto outer = [=](auto param1) {
|
||||
auto inner = [=](auto param2) {
|
||||
invoke_fmt<decltype(param1), decltype(param2)>(Data, Size);
|
||||
};
|
||||
invoke(second, inner);
|
||||
};
|
||||
|
||||
try {
|
||||
invoke(first, outer);
|
||||
} catch (std::exception& /*e*/) {
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
// Formatting library for C++ - Grisu tests
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "gtest.h"
|
||||
|
||||
static bool reported_skipped;
|
||||
|
||||
#undef TEST
|
||||
#define TEST(test_fixture, test_name) \
|
||||
void test_fixture##test_name(); \
|
||||
GTEST_TEST(test_fixture, test_name) { \
|
||||
if (FMT_USE_GRISU) { \
|
||||
test_fixture##test_name(); \
|
||||
} else if (!reported_skipped) { \
|
||||
reported_skipped = true; \
|
||||
fmt::print("Skipping Grisu tests.\n"); \
|
||||
} \
|
||||
} \
|
||||
void test_fixture##test_name()
|
||||
|
||||
TEST(GrisuTest, NaN) {
|
||||
auto nan = std::numeric_limits<double>::quiet_NaN();
|
||||
EXPECT_EQ("nan", fmt::format("{}", nan));
|
||||
EXPECT_EQ("-nan", fmt::format("{}", -nan));
|
||||
}
|
||||
|
||||
TEST(GrisuTest, Inf) {
|
||||
auto inf = std::numeric_limits<double>::infinity();
|
||||
EXPECT_EQ("inf", fmt::format("{}", inf));
|
||||
EXPECT_EQ("-inf", fmt::format("{}", -inf));
|
||||
}
|
||||
|
||||
TEST(GrisuTest, Zero) { EXPECT_EQ("0.0", fmt::format("{}", 0.0)); }
|
||||
|
||||
TEST(GrisuTest, Round) {
|
||||
EXPECT_EQ("1.9156918820264798e-56",
|
||||
fmt::format("{}", 1.9156918820264798e-56));
|
||||
EXPECT_EQ("0.0000", fmt::format("{:.4f}", 7.2809479766055470e-15));
|
||||
}
|
||||
|
||||
TEST(GrisuTest, Prettify) {
|
||||
EXPECT_EQ("0.0001", fmt::format("{}", 1e-4));
|
||||
EXPECT_EQ("1e-05", fmt::format("{}", 1e-5));
|
||||
EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5));
|
||||
EXPECT_EQ("10000000000.0", fmt::format("{}", 1e10));
|
||||
EXPECT_EQ("100000000000.0", fmt::format("{}", 1e11));
|
||||
EXPECT_EQ("12340000000.0", fmt::format("{}", 1234e7));
|
||||
EXPECT_EQ("12.34", fmt::format("{}", 1234e-2));
|
||||
EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6));
|
||||
EXPECT_EQ("0.1", fmt::format("{}", 0.1f));
|
||||
EXPECT_EQ("0.10000000149011612", fmt::format("{}", double(0.1f)));
|
||||
}
|
||||
|
||||
TEST(GrisuTest, ZeroPrecision) { EXPECT_EQ("1", fmt::format("{:.0}", 1.0)); }
|
||||
|
||||
TEST(GrisuTest, Fallback) {
|
||||
EXPECT_EQ("1e+23", fmt::format("{}", 1e23));
|
||||
EXPECT_EQ("9e-265", fmt::format("{}", 9e-265));
|
||||
EXPECT_EQ("5.423717798060526e+125",
|
||||
fmt::format("{}", 5.423717798060526e+125));
|
||||
EXPECT_EQ("1.372371880954233e-288",
|
||||
fmt::format("{}", 1.372371880954233e-288));
|
||||
EXPECT_EQ("55388492.622190244", fmt::format("{}", 55388492.622190244));
|
||||
EXPECT_EQ("2.2506787569811123e-253",
|
||||
fmt::format("{}", 2.2506787569811123e-253));
|
||||
EXPECT_EQ("1103618912042992.8", fmt::format("{}", 1103618912042992.8));
|
||||
// pow(2, -25) - assymetric boundaries:
|
||||
EXPECT_EQ("2.9802322387695312e-08",
|
||||
fmt::format("{}", 2.9802322387695312e-08));
|
||||
}
|
|
@ -145,7 +145,13 @@ std::string read(fmt::file& f, size_t count);
|
|||
read(file, fmt::string_view(expected_content).size()))
|
||||
|
||||
#else
|
||||
# define EXPECT_WRITE(file, statement, expected_output) SUCCEED()
|
||||
# define EXPECT_WRITE(file, statement, expected_output) \
|
||||
do { \
|
||||
(void)(file); \
|
||||
(void)(statement); \
|
||||
(void)(expected_output); \
|
||||
SUCCEED(); \
|
||||
} while (false)
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
template <typename Mock> struct ScopedMock : testing::StrictMock<Mock> {
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "fmt/locale.h"
|
||||
|
||||
#include <complex>
|
||||
|
||||
#include "gmock.h"
|
||||
|
||||
using fmt::detail::max_value;
|
||||
|
@ -50,6 +52,7 @@ TEST(LocaleTest, Format) {
|
|||
EXPECT_EQ("1234567", fmt::format(std::locale(), "{:L}", 1234567));
|
||||
EXPECT_EQ("1~234~567", fmt::format(loc, "{:L}", 1234567));
|
||||
EXPECT_EQ("-1~234~567", fmt::format(loc, "{:L}", -1234567));
|
||||
EXPECT_EQ("-256", fmt::format(loc, "{:L}", -256));
|
||||
fmt::format_arg_store<fmt::format_context, int> as{1234567};
|
||||
EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:L}", fmt::format_args(as)));
|
||||
std::string s;
|
||||
|
@ -106,4 +109,52 @@ TEST(LocaleTest, DoubleFormatter) {
|
|||
EXPECT_STREQ("12,345", buf);
|
||||
}
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <class charT> struct formatter<std::complex<double>, charT> {
|
||||
private:
|
||||
detail::dynamic_format_specs<char> specs_;
|
||||
|
||||
public:
|
||||
typename basic_format_parse_context<charT>::iterator parse(
|
||||
basic_format_parse_context<charT>& ctx) {
|
||||
using handler_type =
|
||||
detail::dynamic_specs_handler<basic_format_parse_context<charT>>;
|
||||
detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
|
||||
detail::type::string_type);
|
||||
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
|
||||
detail::parse_float_type_spec(specs_, ctx.error_handler());
|
||||
return it;
|
||||
}
|
||||
|
||||
template <class FormatContext>
|
||||
typename FormatContext::iterator format(const std::complex<double>& c,
|
||||
FormatContext& ctx) {
|
||||
detail::handle_dynamic_spec<detail::precision_checker>(
|
||||
specs_.precision, specs_.precision_ref, ctx);
|
||||
auto format_specs = std::string();
|
||||
if (specs_.precision > 0)
|
||||
format_specs = fmt::format(".{}", specs_.precision);
|
||||
if (specs_.type)
|
||||
format_specs += specs_.type;
|
||||
auto real = fmt::format(ctx.locale().template get<std::locale>(),
|
||||
"{:" + format_specs + "}", c.real());
|
||||
auto imag = fmt::format(ctx.locale().template get<std::locale>(),
|
||||
"{:" + format_specs + "}", c.imag());
|
||||
auto fill_align_width = std::string();
|
||||
if (specs_.width > 0)
|
||||
fill_align_width = fmt::format(">{}", specs_.width);
|
||||
return format_to(
|
||||
ctx.out(), "{:" + fill_align_width + "}",
|
||||
fmt::format(c.real() != 0 ? "({0}+{1}i)" : "{1}i", real, imag));
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(FormatTest, Complex) {
|
||||
std::string s = fmt::format("{}", std::complex<double>(1, 2));
|
||||
EXPECT_EQ(s, "(1+2i)");
|
||||
EXPECT_EQ(fmt::format("{:.2f}", std::complex<double>(1, 2)), "(1.00+2.00i)");
|
||||
EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), " (1+2i)");
|
||||
}
|
||||
|
||||
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
|
|
|
@ -81,9 +81,9 @@ TEST(UtilTest, FormatWindowsError) {
|
|||
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
||||
fmt::to_string(actual_message));
|
||||
actual_message.resize(0);
|
||||
auto max_size = fmt::detail::max_value<size_t>();
|
||||
auto max_size = fmt::detail::max_value<size_t>() / 2;
|
||||
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS,
|
||||
fmt::string_view(0, max_size));
|
||||
fmt::string_view(nullptr, max_size));
|
||||
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS),
|
||||
fmt::to_string(actual_message));
|
||||
}
|
||||
|
@ -287,26 +287,38 @@ TEST(BufferedFileTest, Fileno) {
|
|||
EXPECT_READ(copy, FILE_CONTENT);
|
||||
}
|
||||
|
||||
TEST(DirectBufferedFileTest, Print) {
|
||||
fmt::direct_buffered_file out(
|
||||
"test-file", fmt::file::WRONLY | fmt::file::CREATE);
|
||||
fmt::print(out, "The answer is {}.\n", 42);
|
||||
TEST(OStreamTest, Move) {
|
||||
fmt::ostream out = fmt::output_file("test-file");
|
||||
fmt::ostream moved(std::move(out));
|
||||
moved.print("hello");
|
||||
}
|
||||
|
||||
TEST(OStreamTest, Print) {
|
||||
fmt::ostream out = fmt::output_file("test-file");
|
||||
out.print("The answer is {}.\n", 42);
|
||||
out.close();
|
||||
file in("test-file", file::RDONLY);
|
||||
EXPECT_READ(in, "The answer is 42.\n");
|
||||
}
|
||||
|
||||
TEST(DirectBufferedFileTest, BufferBoundary) {
|
||||
TEST(OStreamTest, BufferBoundary) {
|
||||
auto str = std::string(4096, 'x');
|
||||
fmt::direct_buffered_file out(
|
||||
"test-file", fmt::file::WRONLY | fmt::file::CREATE);
|
||||
fmt::print(out, "{}", str);
|
||||
fmt::print(out, "{}", str);
|
||||
fmt::ostream out = fmt::output_file("test-file");
|
||||
out.print("{}", str);
|
||||
out.print("{}", str);
|
||||
out.close();
|
||||
file in("test-file", file::RDONLY);
|
||||
EXPECT_READ(in, str + str);
|
||||
}
|
||||
|
||||
TEST(OStreamTest, BufferSize) {
|
||||
fmt::ostream out = fmt::output_file("test-file", fmt::buffer_size=1);
|
||||
out.print("{}", "foo");
|
||||
out.close();
|
||||
file in("test-file", file::RDONLY);
|
||||
EXPECT_READ(in, "foo");
|
||||
}
|
||||
|
||||
TEST(FileTest, DefaultCtor) {
|
||||
file f;
|
||||
EXPECT_EQ(-1, f.descriptor());
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#define FMT_STRING_ALIAS 1
|
||||
#include "fmt/format.h"
|
||||
|
||||
struct test {};
|
||||
|
@ -24,6 +23,7 @@ template <> struct formatter<test> : formatter<int> {
|
|||
#include <sstream>
|
||||
|
||||
#include "fmt/ostream.h"
|
||||
#include "fmt/ranges.h"
|
||||
#include "gmock.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
@ -75,8 +75,8 @@ struct test_arg_formatter
|
|||
|
||||
TEST(OStreamTest, CustomArg) {
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::detail::buffer<char>& base = buffer;
|
||||
fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
|
||||
fmt::format_context ctx(fmt::detail::buffer_appender<char>{buffer},
|
||||
fmt::format_args());
|
||||
fmt::format_specs spec;
|
||||
test_arg_formatter af(ctx, spec);
|
||||
fmt::visit_format_arg(
|
||||
|
@ -150,8 +150,9 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
|
|||
std::streamsize max_streamsize = fmt::detail::max_value<std::streamsize>();
|
||||
if (max_size <= fmt::detail::to_unsigned(max_streamsize)) return;
|
||||
|
||||
struct test_buffer : fmt::detail::buffer<char> {
|
||||
explicit test_buffer(size_t size) { resize(size); }
|
||||
struct test_buffer final : fmt::detail::buffer<char> {
|
||||
explicit test_buffer(size_t size)
|
||||
: fmt::detail::buffer<char>(nullptr, size, size) {}
|
||||
void grow(size_t) {}
|
||||
} buffer(max_size);
|
||||
|
||||
|
@ -289,9 +290,20 @@ std::ostream& operator<<(std::ostream& os,
|
|||
TEST(OStreamTest, FormatExplicitlyConvertibleToStdStringView) {
|
||||
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
|
||||
}
|
||||
|
||||
#endif // FMT_USE_STRING_VIEW
|
||||
|
||||
struct streamable_and_convertible_to_bool {
|
||||
operator bool() const { return true; }
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, streamable_and_convertible_to_bool) {
|
||||
return os << "foo";
|
||||
}
|
||||
|
||||
TEST(OStreamTest, FormatConvertibleToBool) {
|
||||
EXPECT_EQ("foo", fmt::format("{}", streamable_and_convertible_to_bool()));
|
||||
}
|
||||
|
||||
struct copyfmt_test {};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, copyfmt_test) {
|
||||
|
@ -307,3 +319,12 @@ TEST(OStreamTest, CopyFmt) {
|
|||
TEST(OStreamTest, CompileTimeString) {
|
||||
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
|
||||
}
|
||||
|
||||
TEST(OStreamTest, ToString) {
|
||||
EXPECT_EQ("ABC", fmt::to_string(fmt_test::ABC()));
|
||||
}
|
||||
|
||||
TEST(OStreamTest, Range) {
|
||||
auto strs = std::vector<TestString>{TestString("foo"), TestString("bar")};
|
||||
EXPECT_EQ("{foo, bar}", format("{}", strs));
|
||||
}
|
|
@ -54,7 +54,7 @@ TEST(RangesTest, FormatTuple) {
|
|||
TEST(RangesTest, JoinTuple) {
|
||||
// Value tuple args
|
||||
std::tuple<char, int, float> t1 = std::make_tuple('a', 1, 2.0f);
|
||||
EXPECT_EQ("(a, 1, 2.0)", fmt::format("({})", fmt::join(t1, ", ")));
|
||||
EXPECT_EQ("(a, 1, 2)", fmt::format("({})", fmt::join(t1, ", ")));
|
||||
|
||||
// Testing lvalue tuple args
|
||||
int x = 4;
|
||||
|
@ -67,7 +67,7 @@ TEST(RangesTest, JoinTuple) {
|
|||
|
||||
// Single element tuple
|
||||
std::tuple<float> t4{4.0f};
|
||||
EXPECT_EQ("4.0", fmt::format("{}", fmt::join(t4, "/")));
|
||||
EXPECT_EQ("4", fmt::format("{}", fmt::join(t4, "/")));
|
||||
}
|
||||
|
||||
TEST(RangesTest, JoinInitializerList) {
|
||||
|
@ -141,15 +141,63 @@ TEST(RangesTest, FormatStringLike) {
|
|||
#endif // FMT_USE_STRING_VIEW
|
||||
|
||||
struct zstring_sentinel {};
|
||||
|
||||
bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
|
||||
bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
|
||||
|
||||
struct zstring {
|
||||
const char* p;
|
||||
const char* begin() const { return p; }
|
||||
zstring_sentinel end() const { return {}; }
|
||||
};
|
||||
|
||||
TEST(RangesTest, JoinSentinel) {
|
||||
zstring hello{"hello"};
|
||||
EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello));
|
||||
EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_")));
|
||||
}
|
||||
|
||||
// A range that provides non-const only begin()/end() to test fmt::join handles
|
||||
// that
|
||||
//
|
||||
// Some ranges (eg those produced by range-v3's views::filter()) can cache
|
||||
// information during iteration so they only provide non-const begin()/end().
|
||||
template <typename T> class non_const_only_range {
|
||||
private:
|
||||
std::vector<T> vec;
|
||||
|
||||
public:
|
||||
using const_iterator = typename ::std::vector<T>::const_iterator;
|
||||
|
||||
template <typename... Args>
|
||||
explicit non_const_only_range(Args&&... args)
|
||||
: vec(::std::forward<Args>(args)...) {}
|
||||
|
||||
const_iterator begin() { return vec.begin(); }
|
||||
const_iterator end() { return vec.end(); }
|
||||
};
|
||||
|
||||
TEST(RangesTest, JoinRange) {
|
||||
non_const_only_range<int> x(3u, 0);
|
||||
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(x, ",")));
|
||||
EXPECT_EQ(
|
||||
"0,0,0",
|
||||
fmt::format("{}", fmt::join(non_const_only_range<int>(3u, 0), ",")));
|
||||
|
||||
std::vector<int> y(3u, 0);
|
||||
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(y, ",")));
|
||||
EXPECT_EQ("0,0,0",
|
||||
fmt::format("{}", fmt::join(std::vector<int>(3u, 0), ",")));
|
||||
|
||||
const std::vector<int> z(3u, 0);
|
||||
EXPECT_EQ("0,0,0", fmt::format("{}", fmt::join(z, ",")));
|
||||
}
|
||||
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
|
||||
struct unformattable {};
|
||||
|
||||
TEST(RangesTest, UnformattableRange) {
|
||||
EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>,
|
||||
fmt::format_context>::value));
|
||||
}
|
||||
#endif
|
||||
|
|