Merge remote-tracking branch 'zverovich/master'
Conflicts: include/fmt/core.h test/format-dyn-args-test.cc
This commit is contained in:
commit
baae799489
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
|
.vs/
|
||||||
|
|
||||||
*.iml
|
*.iml
|
||||||
.idea/
|
.idea/
|
||||||
|
|||||||
@ -54,7 +54,7 @@ option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
|
|||||||
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
|
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
|
||||||
option(FMT_FUZZ "Generate the fuzz target." OFF)
|
option(FMT_FUZZ "Generate the fuzz target." OFF)
|
||||||
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
||||||
|
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
|
||||||
project(FMT CXX)
|
project(FMT CXX)
|
||||||
|
|
||||||
# Get version from core.h
|
# Get version from core.h
|
||||||
@ -81,6 +81,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
|||||||
|
|
||||||
include(cxx14)
|
include(cxx14)
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
|
include(JoinPaths)
|
||||||
|
|
||||||
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
|
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
|
||||||
if (${index} GREATER -1)
|
if (${index} GREATER -1)
|
||||||
@ -173,7 +174,11 @@ endfunction()
|
|||||||
# Define the fmt library, its includes and the needed defines.
|
# Define the fmt library, its includes and the needed defines.
|
||||||
add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
|
add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
|
||||||
locale.h os.h ostream.h posix.h printf.h ranges.h)
|
locale.h os.h ostream.h posix.h printf.h ranges.h)
|
||||||
set(FMT_SOURCES src/format.cc src/os.cc)
|
if (FMT_OS)
|
||||||
|
set(FMT_SOURCES src/format.cc src/os.cc)
|
||||||
|
else()
|
||||||
|
set(FMT_SOURCES src/format.cc)
|
||||||
|
endif ()
|
||||||
|
|
||||||
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
|
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
|
||||||
add_library(fmt::fmt ALIAS fmt)
|
add_library(fmt::fmt ALIAS fmt)
|
||||||
@ -201,8 +206,9 @@ set_target_properties(fmt PROPERTIES
|
|||||||
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
|
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
|
||||||
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
|
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
|
||||||
|
|
||||||
# Set FMT_LIB_NAME for pkg-config fmt.pc.
|
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
|
||||||
get_target_property(FMT_LIB_NAME fmt OUTPUT_NAME)
|
# property because it's not set by default.
|
||||||
|
set(FMT_LIB_NAME fmt)
|
||||||
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX})
|
set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX})
|
||||||
endif ()
|
endif ()
|
||||||
@ -234,8 +240,8 @@ if (FMT_INSTALL)
|
|||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
include(CMakePackageConfigHelpers)
|
include(CMakePackageConfigHelpers)
|
||||||
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
||||||
"Installation directory for cmake files, relative to "
|
"Installation directory for cmake files, a relative path "
|
||||||
"${CMAKE_INSTALL_PREFIX}.")
|
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
||||||
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
|
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
|
||||||
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
|
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
|
||||||
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
|
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
|
||||||
@ -247,22 +253,26 @@ if (FMT_INSTALL)
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
||||||
"Installation directory for libraries, relative to "
|
"Installation directory for libraries, a relative path "
|
||||||
"${CMAKE_INSTALL_PREFIX}.")
|
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
||||||
|
|
||||||
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRINGS
|
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
|
||||||
"Installation directory for include files, relative to "
|
"Installation directory for include files, a relative path "
|
||||||
"${CMAKE_INSTALL_PREFIX}.")
|
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
||||||
|
|
||||||
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
|
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
|
||||||
"Installation directory for pkgconfig (.pc) files, relative to "
|
"Installation directory for pkgconfig (.pc) files, a relative path "
|
||||||
"${CMAKE_INSTALL_PREFIX}.")
|
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
|
||||||
|
|
||||||
# Generate the version, config and target files into the build directory.
|
# Generate the version, config and target files into the build directory.
|
||||||
write_basic_package_version_file(
|
write_basic_package_version_file(
|
||||||
${version_config}
|
${version_config}
|
||||||
VERSION ${FMT_VERSION}
|
VERSION ${FMT_VERSION}
|
||||||
COMPATIBILITY AnyNewerVersion)
|
COMPATIBILITY AnyNewerVersion)
|
||||||
|
|
||||||
|
join_paths(libdir_for_pc_file "\${exec_prefix}" "${CMAKE_INSTALL_LIBDIR}")
|
||||||
|
join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
|
|
||||||
configure_file(
|
configure_file(
|
||||||
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
|
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
|
||||||
"${pkgconfig}"
|
"${pkgconfig}"
|
||||||
@ -307,6 +317,7 @@ endif ()
|
|||||||
# Control fuzzing independent of the unit tests.
|
# Control fuzzing independent of the unit tests.
|
||||||
if (FMT_FUZZ)
|
if (FMT_FUZZ)
|
||||||
add_subdirectory(test/fuzzing)
|
add_subdirectory(test/fuzzing)
|
||||||
|
target_compile_definitions(fmt PUBLIC FMT_FUZZ)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
|
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
|
||||||
|
|||||||
213
ChangeLog.rst
213
ChangeLog.rst
@ -1,3 +1,216 @@
|
|||||||
|
6.2.0 - 2020-04-05
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* Improved error reporting when trying to format an object of a non-formattable
|
||||||
|
type:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
fmt::format("{}", S());
|
||||||
|
|
||||||
|
now gives::
|
||||||
|
|
||||||
|
include/fmt/core.h:1015:5: error: static_assert failed due to requirement
|
||||||
|
'formattable' "Cannot format argument. To make type T formattable provide a
|
||||||
|
formatter<T> specialization:
|
||||||
|
https://fmt.dev/latest/api.html#formatting-user-defined-types"
|
||||||
|
static_assert(
|
||||||
|
^
|
||||||
|
...
|
||||||
|
note: in instantiation of function template specialization
|
||||||
|
'fmt::v6::format<char [3], S, char>' requested here
|
||||||
|
fmt::format("{}", S());
|
||||||
|
^
|
||||||
|
|
||||||
|
if ``S`` is not formattable.
|
||||||
|
|
||||||
|
* Reduced library size by ~10%.
|
||||||
|
|
||||||
|
* Always print decimal point if ``#`` is specified
|
||||||
|
(`#1476 <https://github.com/fmtlib/fmt/issues/1476>`_,
|
||||||
|
`#1498 <https://github.com/fmtlib/fmt/issues/1498>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
fmt::print("{:#.0f}", 42.0);
|
||||||
|
|
||||||
|
now prints ``42.``
|
||||||
|
|
||||||
|
* Implemented the ``'L'`` specifier for locale-specific numeric formatting to
|
||||||
|
improve compatibility with ``std::format``. The ``'n'`` specifier is now
|
||||||
|
deprecated and will be removed in the next major release.
|
||||||
|
|
||||||
|
* Moved OS-specific APIs such as ``windows_error`` from ``fmt/format.h`` to
|
||||||
|
``fmt/os.h``. You can define ``FMT_DEPRECATED_INCLUDE_OS`` to automatically
|
||||||
|
include ``fmt/os.h`` from ``fmt/format.h`` for compatibility but this will be
|
||||||
|
disabled in the next major release.
|
||||||
|
|
||||||
|
* Added precision overflow detection in floating-point formatting.
|
||||||
|
|
||||||
|
* Implemented detection of invalid use of ``fmt::arg``.
|
||||||
|
|
||||||
|
* Used ``type_identity`` to block unnecessary template argument deduction.
|
||||||
|
Thanks Tim Song.
|
||||||
|
|
||||||
|
* Improved UTF-8 handling
|
||||||
|
(`#1109 <https://github.com/fmtlib/fmt/issues/1109>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
fmt::print("┌{0:─^{2}}┐\n"
|
||||||
|
"│{1: ^{2}}│\n"
|
||||||
|
"└{0:─^{2}}┘\n", "", "Привет, мир!", 20);
|
||||||
|
|
||||||
|
now prints::
|
||||||
|
|
||||||
|
┌────────────────────┐
|
||||||
|
│ Привет, мир! │
|
||||||
|
└────────────────────┘
|
||||||
|
|
||||||
|
on systems that support Unicode.
|
||||||
|
|
||||||
|
* Added experimental dynamic argument storage
|
||||||
|
(`#1170 <https://github.com/fmtlib/fmt/issues/1170>`_,
|
||||||
|
`#1584 <https://github.com/fmtlib/fmt/pull/1584>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
store.push_back("answer");
|
||||||
|
store.push_back(42);
|
||||||
|
fmt::vprint("The {} is {}.\n", store);
|
||||||
|
|
||||||
|
prints::
|
||||||
|
|
||||||
|
The answer is 42.
|
||||||
|
|
||||||
|
Thanks `@vsolontsov-ll (Vladimir Solontsov)
|
||||||
|
<https://github.com/vsolontsov-ll>`_.
|
||||||
|
|
||||||
|
* Made ``fmt::join`` accept ``initializer_list``
|
||||||
|
(`#1591 <https://github.com/fmtlib/fmt/pull/1591>`_).
|
||||||
|
Thanks `@Rapotkinnik (Nikolay Rapotkin) <https://github.com/Rapotkinnik>`_.
|
||||||
|
|
||||||
|
* Fixed handling of empty tuples
|
||||||
|
(`#1588 <https://github.com/fmtlib/fmt/issues/1588>`_).
|
||||||
|
|
||||||
|
* Fixed handling of output iterators in ``format_to_n``
|
||||||
|
(`#1506 <https://github.com/fmtlib/fmt/issues/1506>`_).
|
||||||
|
|
||||||
|
* Fixed formatting of ``std::chrono::duration`` types to wide output
|
||||||
|
(`#1533 <https://github.com/fmtlib/fmt/pull/1533>`_).
|
||||||
|
Thanks `@zeffy (pilao) <https://github.com/zeffy>`_.
|
||||||
|
|
||||||
|
* Added const ``begin`` and ``end`` overload to buffers
|
||||||
|
(`#1553 <https://github.com/fmtlib/fmt/pull/1553>`_).
|
||||||
|
Thanks `@dominicpoeschko <https://github.com/dominicpoeschko>`_.
|
||||||
|
|
||||||
|
* Added the ability to disable floating-point formatting via ``FMT_USE_FLOAT``,
|
||||||
|
``FMT_USE_DOUBLE`` and ``FMT_USE_LONG_DOUBLE`` macros for extremely
|
||||||
|
memory-constrained embedded system
|
||||||
|
(`#1590 <https://github.com/fmtlib/fmt/pull/1590>`_).
|
||||||
|
Thanks `@albaguirre (Alberto Aguirre) <https://github.com/albaguirre>`_.
|
||||||
|
|
||||||
|
* Made ``FMT_STRING`` work with ``constexpr`` ``string_view``
|
||||||
|
(`#1589 <https://github.com/fmtlib/fmt/pull/1589>`_).
|
||||||
|
Thanks `@scramsby (Scott Ramsby) <https://github.com/scramsby>`_.
|
||||||
|
|
||||||
|
* Implemented a minor optimization in the format string parser
|
||||||
|
(`#1560 <https://github.com/fmtlib/fmt/pull/1560>`_).
|
||||||
|
Thanks `@IkarusDeveloper <https://github.com/IkarusDeveloper>`_.
|
||||||
|
|
||||||
|
* Improved attribute detection
|
||||||
|
(`#1469 <https://github.com/fmtlib/fmt/pull/1469>`_,
|
||||||
|
`#1475 <https://github.com/fmtlib/fmt/pull/1475>`_,
|
||||||
|
`#1576 <https://github.com/fmtlib/fmt/pull/1576>`_).
|
||||||
|
Thanks `@federico-busato (Federico) <https://github.com/federico-busato>`_,
|
||||||
|
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
|
||||||
|
`@refnum <https://github.com/refnum>`_.
|
||||||
|
|
||||||
|
* Improved documentation
|
||||||
|
(`#1481 <https://github.com/fmtlib/fmt/pull/1481>`_,
|
||||||
|
`#1523 <https://github.com/fmtlib/fmt/pull/1523>`_).
|
||||||
|
Thanks `@JackBoosY (Jack·Boos·Yu) <https://github.com/JackBoosY>`_,
|
||||||
|
`@imba-tjd (谭九鼎) <https://github.com/imba-tjd>`_.
|
||||||
|
|
||||||
|
* Fixed symbol visibility on Linux when compiling with ``-fvisibility=hidden``
|
||||||
|
(`#1535 <https://github.com/fmtlib/fmt/pull/1535>`_).
|
||||||
|
Thanks `@milianw (Milian Wolff) <https://github.com/milianw>`_.
|
||||||
|
|
||||||
|
* Implemented various build configuration fixes and improvements
|
||||||
|
(`#1264 <https://github.com/fmtlib/fmt/issues/1264>`_,
|
||||||
|
`#1460 <https://github.com/fmtlib/fmt/issues/1460>`_,
|
||||||
|
`#1534 <https://github.com/fmtlib/fmt/pull/1534>`_,
|
||||||
|
`#1536 <https://github.com/fmtlib/fmt/issues/1536>`_,
|
||||||
|
`#1545 <https://github.com/fmtlib/fmt/issues/1545>`_,
|
||||||
|
`#1546 <https://github.com/fmtlib/fmt/pull/1546>`_,
|
||||||
|
`#1566 <https://github.com/fmtlib/fmt/issues/1566>`_,
|
||||||
|
`#1582 <https://github.com/fmtlib/fmt/pull/1582>`_,
|
||||||
|
`#1597 <https://github.com/fmtlib/fmt/issues/1597>`_,
|
||||||
|
`#1598 <https://github.com/fmtlib/fmt/pull/1598>`_).
|
||||||
|
Thanks `@ambitslix (Attila M. Szilagyi) <https://github.com/ambitslix>`_,
|
||||||
|
`@jwillikers (Jordan Williams) <https://github.com/jwillikers>`_,
|
||||||
|
`@stac47 (Laurent Stacul) <https://github.com/stac47>`_.
|
||||||
|
|
||||||
|
* Fixed various warnings and compilation issues
|
||||||
|
(`#1433 <https://github.com/fmtlib/fmt/pull/1433>`_,
|
||||||
|
`#1461 <https://github.com/fmtlib/fmt/issues/1461>`_,
|
||||||
|
`#1470 <https://github.com/fmtlib/fmt/pull/1470>`_,
|
||||||
|
`#1480 <https://github.com/fmtlib/fmt/pull/1480>`_,
|
||||||
|
`#1485 <https://github.com/fmtlib/fmt/pull/1485>`_,
|
||||||
|
`#1492 <https://github.com/fmtlib/fmt/pull/1492>`_,
|
||||||
|
`#1493 <https://github.com/fmtlib/fmt/issues/1493>`_,
|
||||||
|
`#1504 <https://github.com/fmtlib/fmt/issues/1504>`_,
|
||||||
|
`#1505 <https://github.com/fmtlib/fmt/pull/1505>`_,
|
||||||
|
`#1512 <https://github.com/fmtlib/fmt/pull/1512>`_,
|
||||||
|
`#1515 <https://github.com/fmtlib/fmt/issues/1515>`_,
|
||||||
|
`#1516 <https://github.com/fmtlib/fmt/pull/1516>`_,
|
||||||
|
`#1518 <https://github.com/fmtlib/fmt/pull/1518>`_,
|
||||||
|
`#1519 <https://github.com/fmtlib/fmt/pull/1519>`_,
|
||||||
|
`#1520 <https://github.com/fmtlib/fmt/pull/1520>`_,
|
||||||
|
`#1521 <https://github.com/fmtlib/fmt/pull/1521>`_,
|
||||||
|
`#1522 <https://github.com/fmtlib/fmt/pull/1522>`_,
|
||||||
|
`#1524 <https://github.com/fmtlib/fmt/issues/1524>`_,
|
||||||
|
`#1530 <https://github.com/fmtlib/fmt/pull/1530>`_,
|
||||||
|
`#1531 <https://github.com/fmtlib/fmt/issues/1531>`_,
|
||||||
|
`#1532 <https://github.com/fmtlib/fmt/pull/1532>`_,
|
||||||
|
`#1539 <https://github.com/fmtlib/fmt/issues/1539>`_,
|
||||||
|
`#1547 <https://github.com/fmtlib/fmt/issues/1547>`_,
|
||||||
|
`#1548 <https://github.com/fmtlib/fmt/issues/1548>`_,
|
||||||
|
`#1554 <https://github.com/fmtlib/fmt/pull/1554>`_,
|
||||||
|
`#1567 <https://github.com/fmtlib/fmt/issues/1567>`_,
|
||||||
|
`#1568 <https://github.com/fmtlib/fmt/pull/1568>`_,
|
||||||
|
`#1569 <https://github.com/fmtlib/fmt/pull/1569>`_,
|
||||||
|
`#1571 <https://github.com/fmtlib/fmt/pull/1571>`_,
|
||||||
|
`#1573 <https://github.com/fmtlib/fmt/pull/1573>`_,
|
||||||
|
`#1575 <https://github.com/fmtlib/fmt/pull/1575>`_,
|
||||||
|
`#1581 <https://github.com/fmtlib/fmt/pull/1581>`_,
|
||||||
|
`#1583 <https://github.com/fmtlib/fmt/issues/1583>`_,
|
||||||
|
`#1586 <https://github.com/fmtlib/fmt/issues/1586>`_,
|
||||||
|
`#1587 <https://github.com/fmtlib/fmt/issues/1587>`_,
|
||||||
|
`#1594 <https://github.com/fmtlib/fmt/issues/1594>`_,
|
||||||
|
`#1596 <https://github.com/fmtlib/fmt/pull/1596>`_,
|
||||||
|
`#1604 <https://github.com/fmtlib/fmt/issues/1604>`_,
|
||||||
|
`#1606 <https://github.com/fmtlib/fmt/pull/1606>`_,
|
||||||
|
`#1607 <https://github.com/fmtlib/fmt/issues/1607>`_,
|
||||||
|
`#1609 <https://github.com/fmtlib/fmt/issues/1609>`_).
|
||||||
|
Thanks `@marti4d (Chris Martin) <https://github.com/marti4d>`_,
|
||||||
|
`@iPherian <https://github.com/iPherian>`_,
|
||||||
|
`@parkertomatoes <https://github.com/parkertomatoes>`_,
|
||||||
|
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
|
||||||
|
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
|
||||||
|
`@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
|
||||||
|
`@torsten48 <https://github.com/torsten48>`_,
|
||||||
|
`@tohammer (Tobias Hammer) <https://github.com/tohammer>`_,
|
||||||
|
`@lefticus (Jason Turner) <https://github.com/lefticus>`_,
|
||||||
|
`@ryusakki (Haise) <https://github.com/ryusakki>`_,
|
||||||
|
`@adnsv (Alex Denisov) <https://github.com/adnsv>`_,
|
||||||
|
`@fghzxm <https://github.com/fghzxm>`_,
|
||||||
|
`@refnum <https://github.com/refnum>`_,
|
||||||
|
`@pramodk (Pramod Kumbhar) <https://github.com/pramodk>`_,
|
||||||
|
`@Spirrwell <https://github.com/Spirrwell>`_,
|
||||||
|
`@scramsby (Scott Ramsby) <https://github.com/scramsby>`_.
|
||||||
|
|
||||||
6.1.2 - 2019-12-11
|
6.1.2 - 2019-12-11
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|||||||
41
README.rst
41
README.rst
@ -264,8 +264,8 @@ or the bloat test::
|
|||||||
Projects using this library
|
Projects using this library
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
* `0 A.D. <https://play0ad.com/>`_: A free, open-source, cross-platform real-time
|
* `0 A.D. <https://play0ad.com/>`_: A free, open-source, cross-platform
|
||||||
strategy game
|
real-time strategy game
|
||||||
|
|
||||||
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
||||||
An open-source library for mathematical programming
|
An open-source library for mathematical programming
|
||||||
@ -282,6 +282,16 @@ Projects using this library
|
|||||||
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
|
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
|
||||||
vehicle
|
vehicle
|
||||||
|
|
||||||
|
* `Drake <https://drake.mit.edu/>`_: A planning, control, and analysis toolbox
|
||||||
|
for nonlinear dynamical systems (MIT)
|
||||||
|
|
||||||
|
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
|
||||||
|
(Lyft)
|
||||||
|
|
||||||
|
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
||||||
|
|
||||||
|
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
|
||||||
|
|
||||||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||||
Player vs Player Gaming Network with tweaks
|
Player vs Player Gaming Network with tweaks
|
||||||
|
|
||||||
@ -293,25 +303,20 @@ Projects using this library
|
|||||||
|
|
||||||
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
|
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
|
||||||
|
|
||||||
* `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
|
|
||||||
(Lyft)
|
|
||||||
|
|
||||||
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
|
||||||
|
|
||||||
* `MongoDB <https://mongodb.com/>`_: Distributed document database
|
* `MongoDB <https://mongodb.com/>`_: Distributed document database
|
||||||
|
|
||||||
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
|
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
|
||||||
generate randomized datasets
|
generate randomized datasets
|
||||||
|
|
||||||
* `OpenSpace <https://openspaceproject.com/>`_: An open-source astrovisualization
|
* `OpenSpace <https://openspaceproject.com/>`_: An open-source
|
||||||
framework
|
astrovisualization framework
|
||||||
|
|
||||||
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
|
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
|
||||||
An MMO server, compatible with most Ultima Online clients
|
An MMO server, compatible with most Ultima Online clients
|
||||||
|
|
||||||
|
* `PyTorch <https://github.com/pytorch/pytorch>`_: An open-source machine
|
||||||
|
learning library
|
||||||
|
|
||||||
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
|
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
|
||||||
associative database
|
associative database
|
||||||
|
|
||||||
@ -320,13 +325,14 @@ Projects using this library
|
|||||||
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
|
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
|
||||||
proxy
|
proxy
|
||||||
|
|
||||||
|
* `redpanda <https://vectorized.io/redpanda>`_: A 10x faster Kafka® replacement
|
||||||
|
for mission critical systems written in C++
|
||||||
|
|
||||||
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
|
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
|
||||||
library
|
library
|
||||||
|
|
||||||
* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_:
|
* `Salesforce Analytics Cloud
|
||||||
Small crossplatform 2D graphic engine
|
<https://www.salesforce.com/analytics-cloud/overview/>`_:
|
||||||
|
|
||||||
* `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
|
||||||
@ -344,6 +350,9 @@ Projects using this library
|
|||||||
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
|
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
|
||||||
MMORPG framework
|
MMORPG framework
|
||||||
|
|
||||||
|
* `Windows Terminal <https://github.com/microsoft/terminal>`_: The new Windows
|
||||||
|
Terminal
|
||||||
|
|
||||||
`More... <https://github.com/search?q=fmtlib&type=Code>`_
|
`More... <https://github.com/search?q=fmtlib&type=Code>`_
|
||||||
|
|
||||||
If you are aware of other projects using this library, please let me know
|
If you are aware of other projects using this library, please let me know
|
||||||
|
|||||||
21
doc/api.rst
21
doc/api.rst
@ -9,7 +9,8 @@ The {fmt} library API consists of the following parts:
|
|||||||
* :ref:`fmt/core.h <core-api>`: the core API providing argument handling
|
* :ref:`fmt/core.h <core-api>`: the core API providing argument handling
|
||||||
facilities and a lightweight subset of formatting functions
|
facilities and a lightweight subset of formatting functions
|
||||||
* :ref:`fmt/format.h <format-api>`: the full format API providing compile-time
|
* :ref:`fmt/format.h <format-api>`: the full format API providing compile-time
|
||||||
format string checks, output iterator and user-defined type support
|
format string checks, wide string, output iterator and user-defined type
|
||||||
|
support
|
||||||
* :ref:`fmt/ranges.h <ranges-api>`: additional formatting support for ranges
|
* :ref:`fmt/ranges.h <ranges-api>`: additional formatting support for ranges
|
||||||
and tuples
|
and tuples
|
||||||
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
|
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
|
||||||
@ -43,7 +44,7 @@ participate in an overload resolution if the latter is not a string.
|
|||||||
.. _format:
|
.. _format:
|
||||||
|
|
||||||
.. doxygenfunction:: format(const S&, Args&&...)
|
.. doxygenfunction:: format(const S&, Args&&...)
|
||||||
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<Char>>)
|
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<type_identity_t<Char>>>)
|
||||||
|
|
||||||
.. _print:
|
.. _print:
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ Format API
|
|||||||
==========
|
==========
|
||||||
|
|
||||||
``fmt/format.h`` defines the full format API providing compile-time format
|
``fmt/format.h`` defines the full format API providing compile-time format
|
||||||
string checks, output iterator and user-defined type support.
|
string checks, wide string, output iterator and user-defined type support.
|
||||||
|
|
||||||
Compile-time Format String Checks
|
Compile-time Format String Checks
|
||||||
---------------------------------
|
---------------------------------
|
||||||
@ -178,8 +179,7 @@ example::
|
|||||||
|
|
||||||
enum class color {red, green, blue};
|
enum class color {red, green, blue};
|
||||||
|
|
||||||
template <>
|
template <> struct fmt::formatter<color>: formatter<string_view> {
|
||||||
struct fmt::formatter<color>: formatter<string_view> {
|
|
||||||
// parse is inherited from formatter<string_view>.
|
// parse is inherited from formatter<string_view>.
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(color c, FormatContext& ctx) {
|
auto format(color c, FormatContext& ctx) {
|
||||||
@ -193,6 +193,15 @@ example::
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Since ``parse`` is inherited from ``formatter<string_view>`` it will recognize
|
||||||
|
all string format specifications, for example
|
||||||
|
|
||||||
|
.. code-block:: c++
|
||||||
|
|
||||||
|
fmt::format("{:>10}", color::blue)
|
||||||
|
|
||||||
|
will return ``" blue"``.
|
||||||
|
|
||||||
You can also write a formatter for a hierarchy of classes::
|
You can also write a formatter for a hierarchy of classes::
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
@ -399,7 +408,7 @@ formatting::
|
|||||||
|
|
||||||
std::time_t t = std::time(nullptr);
|
std::time_t t = std::time(nullptr);
|
||||||
// Prints "The date is 2016-04-29." (with the current date)
|
// Prints "The date is 2016-04-29." (with the current date)
|
||||||
fmt::print("The date is {:%Y-%m-%d}.", *std::localtime(&t));
|
fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t));
|
||||||
|
|
||||||
The format string syntax is described in the documentation of
|
The format string syntax is described in the documentation of
|
||||||
`strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_.
|
`strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_.
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
|
|||||||
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
|
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
|
||||||
from distutils.version import LooseVersion
|
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']
|
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']
|
||||||
|
|
||||||
def pip_install(package, commit=None, **kwargs):
|
def pip_install(package, commit=None, **kwargs):
|
||||||
"Install package using pip."
|
"Install package using pip."
|
||||||
|
|||||||
@ -81,8 +81,8 @@ The general form of a *standard format specifier* is:
|
|||||||
sign: "+" | "-" | " "
|
sign: "+" | "-" | " "
|
||||||
width: `integer` | "{" `arg_id` "}"
|
width: `integer` | "{" `arg_id` "}"
|
||||||
precision: `integer` | "{" `arg_id` "}"
|
precision: `integer` | "{" `arg_id` "}"
|
||||||
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
|
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "L" | "p" | "s"
|
||||||
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"
|
int_type: "b" | "B" | "d" | "o" | "x" | "X"
|
||||||
|
|
||||||
The *fill* character can be any Unicode code point other than ``'{'`` or
|
The *fill* character can be any Unicode code point other than ``'{'`` or
|
||||||
``'}'``. The presence of a fill character is signaled by the character following
|
``'}'``. The presence of a fill character is signaled by the character following
|
||||||
@ -143,7 +143,7 @@ conversions, trailing zeros are not removed from the result.
|
|||||||
.. ifconfig:: False
|
.. ifconfig:: False
|
||||||
|
|
||||||
The ``','`` option signals the use of a comma for a thousands separator.
|
The ``','`` option signals the use of a comma for a thousands separator.
|
||||||
For a locale aware separator, use the ``'n'`` integer presentation type
|
For a locale aware separator, use the ``'L'`` integer presentation type
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
*width* is a decimal integer defining the minimum field width. If not
|
*width* is a decimal integer defining the minimum field width. If not
|
||||||
@ -214,9 +214,9 @@ The available integer presentation types are:
|
|||||||
| | ``'#'`` option with this type adds the prefix ``"0X"`` |
|
| | ``'#'`` option with this type adds the prefix ``"0X"`` |
|
||||||
| | to the output value. |
|
| | to the output value. |
|
||||||
+---------+----------------------------------------------------------+
|
+---------+----------------------------------------------------------+
|
||||||
| ``'n'`` | Number. This is the same as ``'d'``, except that it uses |
|
| ``'L'`` | Locale-specific format. This is the same as ``'d'``, |
|
||||||
| | the current locale setting to insert the appropriate |
|
| | except that it uses the current locale setting to insert |
|
||||||
| | number separator characters. |
|
| | the appropriate number separator characters. |
|
||||||
+---------+----------------------------------------------------------+
|
+---------+----------------------------------------------------------+
|
||||||
| none | The same as ``'d'``. |
|
| none | The same as ``'d'``. |
|
||||||
+---------+----------------------------------------------------------+
|
+---------+----------------------------------------------------------+
|
||||||
@ -261,9 +261,9 @@ The available presentation types for floating-point values are:
|
|||||||
| | ``'E'`` if the number gets too large. The |
|
| | ``'E'`` if the number gets too large. The |
|
||||||
| | representations of infinity and NaN are uppercased, too. |
|
| | representations of infinity and NaN are uppercased, too. |
|
||||||
+---------+----------------------------------------------------------+
|
+---------+----------------------------------------------------------+
|
||||||
| ``'n'`` | Number. This is the same as ``'g'``, except that it uses |
|
| ``'L'`` | Locale-specific format. This is the same as ``'g'``, |
|
||||||
| | the current locale setting to insert the appropriate |
|
| | except that it uses the current locale setting to insert |
|
||||||
| | number separator characters. |
|
| | the appropriate number separator characters. |
|
||||||
+---------+----------------------------------------------------------+
|
+---------+----------------------------------------------------------+
|
||||||
| none | Similar to ``'g'``, except that fixed-point notation, |
|
| none | Similar to ``'g'``, except that fixed-point notation, |
|
||||||
| | when used, has at least one digit past the decimal |
|
| | when used, has at least one digit past the decimal |
|
||||||
@ -403,7 +403,7 @@ Using the comma as a thousands separator::
|
|||||||
|
|
||||||
#include <fmt/locale.h>
|
#include <fmt/locale.h>
|
||||||
|
|
||||||
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:n}", 1234567890);
|
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
|
||||||
// s == "1,234,567,890"
|
// s == "1,234,567,890"
|
||||||
|
|
||||||
.. ifconfig:: False
|
.. ifconfig:: False
|
||||||
|
|||||||
@ -768,17 +768,23 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
|
|||||||
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
|
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
|
||||||
val);
|
val);
|
||||||
}
|
}
|
||||||
|
template <typename Char, typename OutputIt>
|
||||||
|
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
|
||||||
|
return std::copy(unit.begin(), unit.end(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
|
||||||
|
// This works when wchar_t is UTF-32 because units only contain characters
|
||||||
|
// that have the same representation in UTF-16 and UTF-32.
|
||||||
|
utf8_to_utf16 u(unit);
|
||||||
|
return std::copy(u.c_str(), u.c_str() + u.size(), out);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char, typename Period, typename OutputIt>
|
template <typename Char, typename Period, typename OutputIt>
|
||||||
OutputIt format_duration_unit(OutputIt out) {
|
OutputIt format_duration_unit(OutputIt out) {
|
||||||
if (const char* unit = get_units<Period>()) {
|
if (const char* unit = get_units<Period>())
|
||||||
string_view s(unit);
|
return copy_unit(string_view(unit), out, Char());
|
||||||
if (const_check(std::is_same<Char, wchar_t>())) {
|
|
||||||
utf8_to_utf16 u(s);
|
|
||||||
return std::copy(u.c_str(), u.c_str() + u.size(), out);
|
|
||||||
}
|
|
||||||
return std::copy(s.begin(), s.end(), out);
|
|
||||||
}
|
|
||||||
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
|
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
|
||||||
if (Period::den == 1) return format_to(out, num_f, Period::num);
|
if (Period::den == 1) return format_to(out, num_f, Period::num);
|
||||||
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
|
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
|
||||||
|
|||||||
@ -412,7 +412,7 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
|
|
||||||
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
||||||
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
||||||
return buffer + std::strlen(buffer);
|
return buffer + std::char_traits<Char>::length(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -576,7 +576,9 @@ OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
|
FMT_ENABLE_IF(
|
||||||
|
internal::is_output_iterator<OutputIt>::value&& std::is_base_of<
|
||||||
|
internal::basic_compiled_format, CompiledFormat>::value)>
|
||||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||||
const CompiledFormat& cf,
|
const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -22,8 +22,14 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
# if defined(NOMINMAX)
|
||||||
|
# include <windows.h>
|
||||||
|
# else
|
||||||
|
# define NOMINMAX
|
||||||
|
# include <windows.h>
|
||||||
|
# undef NOMINMAX
|
||||||
|
# endif
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
# include <windows.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@ -317,6 +323,8 @@ const char basic_data<T>::background_color[] = "\x1b[48;2;";
|
|||||||
template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
|
template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
|
||||||
template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
|
template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
|
||||||
template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
|
template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
|
||||||
|
template <typename T>
|
||||||
|
const char basic_data<T>::padding_shifts[] = {31, 31, 0, 1, 0};
|
||||||
|
|
||||||
template <typename T> struct bits {
|
template <typename T> struct bits {
|
||||||
static FMT_CONSTEXPR_DECL const int value =
|
static FMT_CONSTEXPR_DECL const int value =
|
||||||
@ -507,20 +515,23 @@ class bigint {
|
|||||||
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
basic_memory_buffer<bigit, bigits_capacity> bigits_;
|
||||||
int exp_;
|
int exp_;
|
||||||
|
|
||||||
|
bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
|
||||||
|
bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
|
||||||
|
|
||||||
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
|
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
|
||||||
|
|
||||||
friend struct formatter<bigint>;
|
friend struct formatter<bigint>;
|
||||||
|
|
||||||
void subtract_bigits(int index, bigit other, bigit& borrow) {
|
void subtract_bigits(int index, bigit other, bigit& borrow) {
|
||||||
auto result = static_cast<double_bigit>(bigits_[index]) - other - borrow;
|
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
|
||||||
bigits_[index] = static_cast<bigit>(result);
|
(*this)[index] = static_cast<bigit>(result);
|
||||||
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void remove_leading_zeros() {
|
void remove_leading_zeros() {
|
||||||
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
int num_bigits = static_cast<int>(bigits_.size()) - 1;
|
||||||
while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits;
|
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
|
||||||
bigits_.resize(num_bigits + 1);
|
bigits_.resize(to_unsigned(num_bigits + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes *this -= other assuming aligned bigints and *this >= other.
|
// Computes *this -= other assuming aligned bigints and *this >= other.
|
||||||
@ -591,7 +602,7 @@ class bigint {
|
|||||||
|
|
||||||
int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
|
int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
|
||||||
|
|
||||||
bigint& operator<<=(int shift) {
|
FMT_NOINLINE bigint& operator<<=(int shift) {
|
||||||
assert(shift >= 0);
|
assert(shift >= 0);
|
||||||
exp_ += shift / bigit_bits;
|
exp_ += shift / bigit_bits;
|
||||||
shift %= bigit_bits;
|
shift %= bigit_bits;
|
||||||
@ -621,7 +632,7 @@ class bigint {
|
|||||||
int end = i - j;
|
int end = i - j;
|
||||||
if (end < 0) end = 0;
|
if (end < 0) end = 0;
|
||||||
for (; i >= end; --i, --j) {
|
for (; i >= end; --i, --j) {
|
||||||
bigit lhs_bigit = lhs.bigits_[i], rhs_bigit = rhs.bigits_[j];
|
bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
|
||||||
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
|
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
|
||||||
}
|
}
|
||||||
if (i != j) return i > j ? 1 : -1;
|
if (i != j) return i > j ? 1 : -1;
|
||||||
@ -636,7 +647,7 @@ class bigint {
|
|||||||
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
|
||||||
if (max_lhs_bigits > num_rhs_bigits) return 1;
|
if (max_lhs_bigits > num_rhs_bigits) return 1;
|
||||||
auto get_bigit = [](const bigint& n, int i) -> bigit {
|
auto get_bigit = [](const bigint& n, int i) -> bigit {
|
||||||
return i >= n.exp_ && i < n.num_bigits() ? n.bigits_[i - n.exp_] : 0;
|
return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
|
||||||
};
|
};
|
||||||
double_bigit borrow = 0;
|
double_bigit borrow = 0;
|
||||||
int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
|
int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
|
||||||
@ -676,7 +687,7 @@ class bigint {
|
|||||||
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
|
||||||
int num_bigits = static_cast<int>(bigits_.size());
|
int num_bigits = static_cast<int>(bigits_.size());
|
||||||
int num_result_bigits = 2 * num_bigits;
|
int num_result_bigits = 2 * num_bigits;
|
||||||
bigits_.resize(num_result_bigits);
|
bigits_.resize(to_unsigned(num_result_bigits));
|
||||||
using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
|
using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
|
||||||
auto sum = accumulator_t();
|
auto sum = accumulator_t();
|
||||||
for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
|
for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
|
||||||
@ -686,7 +697,7 @@ class bigint {
|
|||||||
// Most terms are multiplied twice which can be optimized in the future.
|
// Most terms are multiplied twice which can be optimized in the future.
|
||||||
sum += static_cast<double_bigit>(n[i]) * n[j];
|
sum += static_cast<double_bigit>(n[i]) * n[j];
|
||||||
}
|
}
|
||||||
bigits_[bigit_index] = static_cast<bigit>(sum);
|
(*this)[bigit_index] = static_cast<bigit>(sum);
|
||||||
sum >>= bits<bigit>::value; // Compute the carry.
|
sum >>= bits<bigit>::value; // Compute the carry.
|
||||||
}
|
}
|
||||||
// Do the same for the top half.
|
// Do the same for the top half.
|
||||||
@ -694,7 +705,7 @@ class bigint {
|
|||||||
++bigit_index) {
|
++bigit_index) {
|
||||||
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
|
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
|
||||||
sum += static_cast<double_bigit>(n[i++]) * n[j--];
|
sum += static_cast<double_bigit>(n[i++]) * n[j--];
|
||||||
bigits_[bigit_index] = static_cast<bigit>(sum);
|
(*this)[bigit_index] = static_cast<bigit>(sum);
|
||||||
sum >>= bits<bigit>::value;
|
sum >>= bits<bigit>::value;
|
||||||
}
|
}
|
||||||
--num_result_bigits;
|
--num_result_bigits;
|
||||||
@ -708,11 +719,11 @@ class bigint {
|
|||||||
FMT_ASSERT(this != &divisor, "");
|
FMT_ASSERT(this != &divisor, "");
|
||||||
if (compare(*this, divisor) < 0) return 0;
|
if (compare(*this, divisor) < 0) return 0;
|
||||||
int num_bigits = static_cast<int>(bigits_.size());
|
int num_bigits = static_cast<int>(bigits_.size());
|
||||||
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1] != 0, "");
|
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
|
||||||
int exp_difference = exp_ - divisor.exp_;
|
int exp_difference = exp_ - divisor.exp_;
|
||||||
if (exp_difference > 0) {
|
if (exp_difference > 0) {
|
||||||
// Align bigints by adding trailing zeros to simplify subtraction.
|
// Align bigints by adding trailing zeros to simplify subtraction.
|
||||||
bigits_.resize(num_bigits + exp_difference);
|
bigits_.resize(to_unsigned(num_bigits + exp_difference));
|
||||||
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
|
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
|
||||||
bigits_[j] = bigits_[i];
|
bigits_[j] = bigits_[i];
|
||||||
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
|
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
|
||||||
@ -1023,7 +1034,7 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
|
|||||||
if (result > 0 || (result == 0 && (digit % 2) != 0))
|
if (result > 0 || (result == 0 && (digit % 2) != 0))
|
||||||
++data[num_digits - 1];
|
++data[num_digits - 1];
|
||||||
}
|
}
|
||||||
buf.resize(num_digits);
|
buf.resize(to_unsigned(num_digits));
|
||||||
exp10 -= num_digits - 1;
|
exp10 -= num_digits - 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1142,7 +1153,7 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
auto begin = buf.data() + offset;
|
auto begin = buf.data() + offset;
|
||||||
auto capacity = buf.capacity() - offset;
|
auto capacity = buf.capacity() - offset;
|
||||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
#ifdef FMT_FUZZ
|
||||||
if (precision > 100000)
|
if (precision > 100000)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"fuzz mode - avoid large allocation inside snprintf");
|
"fuzz mode - avoid large allocation inside snprintf");
|
||||||
@ -1157,7 +1168,7 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially.
|
buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
unsigned size = to_unsigned(result);
|
auto size = to_unsigned(result);
|
||||||
// Size equal to capacity means that the last character was truncated.
|
// Size equal to capacity means that the last character was truncated.
|
||||||
if (size >= capacity) {
|
if (size >= capacity) {
|
||||||
buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'.
|
buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'.
|
||||||
@ -1175,7 +1186,7 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
--p;
|
--p;
|
||||||
} while (is_digit(*p));
|
} while (is_digit(*p));
|
||||||
int fraction_size = static_cast<int>(end - p - 1);
|
int fraction_size = static_cast<int>(end - p - 1);
|
||||||
std::memmove(p, p + 1, fraction_size);
|
std::memmove(p, p + 1, to_unsigned(fraction_size));
|
||||||
buf.resize(size - 1);
|
buf.resize(size - 1);
|
||||||
return -fraction_size;
|
return -fraction_size;
|
||||||
}
|
}
|
||||||
@ -1204,9 +1215,9 @@ int snprintf_float(T value, int precision, float_specs specs,
|
|||||||
while (*fraction_end == '0') --fraction_end;
|
while (*fraction_end == '0') --fraction_end;
|
||||||
// Move the fractional part left to get rid of the decimal point.
|
// Move the fractional part left to get rid of the decimal point.
|
||||||
fraction_size = static_cast<int>(fraction_end - begin - 1);
|
fraction_size = static_cast<int>(fraction_end - begin - 1);
|
||||||
std::memmove(begin + 1, begin + 2, fraction_size);
|
std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size));
|
||||||
}
|
}
|
||||||
buf.resize(fraction_size + offset + 1);
|
buf.resize(to_unsigned(fraction_size) + offset + 1);
|
||||||
return exp - fraction_size;
|
return exp - fraction_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1277,7 +1288,7 @@ template <> struct formatter<internal::bigint> {
|
|||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
bool first = true;
|
bool first = true;
|
||||||
for (auto i = n.bigits_.size(); i > 0; --i) {
|
for (auto i = n.bigits_.size(); i > 0; --i) {
|
||||||
auto value = n.bigits_[i - 1];
|
auto value = n.bigits_[i - 1u];
|
||||||
if (first) {
|
if (first) {
|
||||||
out = format_to(out, "{:x}", value);
|
out = format_to(out, "{:x}", value);
|
||||||
first = false;
|
first = false;
|
||||||
@ -1313,7 +1324,7 @@ FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
|||||||
}
|
}
|
||||||
if (auto num_chars_left = s.data() + s.size() - p) {
|
if (auto num_chars_left = s.data() + s.size() - p) {
|
||||||
char buf[2 * block_size - 1] = {};
|
char buf[2 * block_size - 1] = {};
|
||||||
memcpy(buf, p, num_chars_left);
|
memcpy(buf, p, to_unsigned(num_chars_left));
|
||||||
p = buf;
|
p = buf;
|
||||||
do {
|
do {
|
||||||
p = transcode(p);
|
p = transcode(p);
|
||||||
@ -1368,7 +1379,7 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
|
|||||||
if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
|
if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
|
||||||
u16.c_str(), static_cast<DWORD>(u16.size()), &written,
|
u16.c_str(), static_cast<DWORD>(u16.size()), &written,
|
||||||
nullptr)) {
|
nullptr)) {
|
||||||
throw format_error("failed to write to console");
|
FMT_THROW(format_error("failed to write to console"));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -15,11 +15,10 @@
|
|||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <clocale> // for locale_t
|
#include <clocale> // for locale_t
|
||||||
|
#include <cstddef>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib> // for strtod_l
|
#include <cstdlib> // for strtod_l
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||||
#endif
|
#endif
|
||||||
@ -346,21 +345,15 @@ long getpagesize();
|
|||||||
|
|
||||||
#ifdef FMT_LOCALE
|
#ifdef FMT_LOCALE
|
||||||
// A "C" numeric locale.
|
// A "C" numeric locale.
|
||||||
class Locale {
|
class locale {
|
||||||
private:
|
private:
|
||||||
# ifdef _WIN32
|
# ifdef _WIN32
|
||||||
using locale_t = _locale_t;
|
using locale_t = _locale_t;
|
||||||
|
|
||||||
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
static void freelocale(locale_t loc) { _free_locale(loc); }
|
||||||
|
|
||||||
static locale_t newlocale(int category_mask, const char* locale, locale_t) {
|
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
|
||||||
return _create_locale(category_mask, locale);
|
return _strtod_l(nptr, endptr, loc);
|
||||||
}
|
|
||||||
|
|
||||||
static void freelocale(locale_t locale) { _free_locale(locale); }
|
|
||||||
|
|
||||||
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
|
|
||||||
return _strtod_l(nptr, endptr, locale);
|
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
@ -368,13 +361,18 @@ class Locale {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
using type = locale_t;
|
using type = locale_t;
|
||||||
Locale(const Locale&) = delete;
|
locale(const locale&) = delete;
|
||||||
void operator=(const Locale&) = delete;
|
void operator=(const locale&) = delete;
|
||||||
|
|
||||||
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
|
locale() {
|
||||||
|
# ifndef _WIN32
|
||||||
|
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
|
||||||
|
# else
|
||||||
|
locale_ = _create_locale(LC_NUMERIC, "C");
|
||||||
|
# endif
|
||||||
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
|
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
|
||||||
}
|
}
|
||||||
~Locale() { freelocale(locale_); }
|
~locale() { freelocale(locale_); }
|
||||||
|
|
||||||
type get() const { return locale_; }
|
type get() const { return locale_; }
|
||||||
|
|
||||||
@ -387,6 +385,7 @@ class Locale {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
using Locale FMT_DEPRECATED_ALIAS = locale;
|
||||||
#endif // FMT_LOCALE
|
#endif // FMT_LOCALE
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
|||||||
@ -9,9 +9,14 @@
|
|||||||
#define FMT_OSTREAM_H_
|
#define FMT_OSTREAM_H_
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
template <typename Char> class basic_printf_parse_context;
|
||||||
|
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||||
@ -93,9 +98,9 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||||||
locale_ref loc = locale_ref()) {
|
locale_ref loc = locale_ref()) {
|
||||||
formatbuf<Char> format_buf(buf);
|
formatbuf<Char> format_buf(buf);
|
||||||
std::basic_ostream<Char> output(&format_buf);
|
std::basic_ostream<Char> output(&format_buf);
|
||||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
if (loc) output.imbue(loc.get<std::locale>());
|
if (loc) output.imbue(loc.get<std::locale>());
|
||||||
#endif
|
#endif
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
output << value;
|
output << value;
|
||||||
buf.resize(buf.size());
|
buf.resize(buf.size());
|
||||||
@ -104,14 +109,32 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||||
: formatter<basic_string_view<Char>, Char> {
|
: private formatter<basic_string_view<Char>, Char> {
|
||||||
template <typename Context>
|
auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()) {
|
||||||
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
|
return formatter<basic_string_view<Char>, Char>::parse(ctx);
|
||||||
|
}
|
||||||
|
template <typename ParseCtx,
|
||||||
|
FMT_ENABLE_IF(std::is_same<
|
||||||
|
ParseCtx, basic_printf_parse_context<Char>>::value)>
|
||||||
|
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
||||||
|
-> OutputIt {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
format_value(buffer, value, ctx.locale());
|
format_value(buffer, value, ctx.locale());
|
||||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||||
}
|
}
|
||||||
|
template <typename OutputIt>
|
||||||
|
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
||||||
|
-> OutputIt {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
format_value(buffer, value, ctx.locale());
|
||||||
|
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
|
|||||||
@ -141,6 +141,13 @@ template <typename Context> class char_converter {
|
|||||||
void operator()(T) {} // No conversion needed for non-integral types.
|
void operator()(T) {} // No conversion needed for non-integral types.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// An argument visitor that return a pointer to a C string if argument is a
|
||||||
|
// string or null otherwise.
|
||||||
|
template <typename Char> struct get_cstring {
|
||||||
|
template <typename T> const Char* operator()(T) { return nullptr; }
|
||||||
|
const Char* operator()(const Char* s) { return s; }
|
||||||
|
};
|
||||||
|
|
||||||
// Checks if an argument is a valid printf width specifier and sets
|
// Checks if an argument is a valid printf width specifier and sets
|
||||||
// left alignment if it is negative.
|
// left alignment if it is negative.
|
||||||
template <typename Char> class printf_width_handler {
|
template <typename Char> class printf_width_handler {
|
||||||
@ -172,23 +179,27 @@ template <typename Char> class printf_width_handler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename Context>
|
template <typename Char, typename Context>
|
||||||
void printf(buffer<Char>& buf, basic_string_view<Char> format,
|
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||||
basic_format_args<Context> args) {
|
basic_format_args<Context> args) {
|
||||||
Context(std::back_inserter(buf), format, args).format();
|
Context(std::back_inserter(buf), format, args).format();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Char, typename Context>
|
|
||||||
internal::truncating_iterator<OutputIt> printf(
|
|
||||||
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
|
|
||||||
basic_format_args<Context> args) {
|
|
||||||
return Context(it, format, args).format();
|
|
||||||
}
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
using internal::printf; // For printing into memory_buffer.
|
// For printing into memory_buffer.
|
||||||
|
template <typename Char, typename Context>
|
||||||
|
FMT_DEPRECATED void printf(internal::buffer<Char>& buf,
|
||||||
|
basic_string_view<Char> format,
|
||||||
|
basic_format_args<Context> args) {
|
||||||
|
return internal::vprintf(buf, format, args);
|
||||||
|
}
|
||||||
|
using internal::vprintf;
|
||||||
|
|
||||||
template <typename Range> class printf_arg_formatter;
|
template <typename Range> class printf_arg_formatter;
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
class basic_printf_parse_context : public basic_format_parse_context<Char> {
|
||||||
|
using basic_format_parse_context<Char>::basic_format_parse_context;
|
||||||
|
};
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -324,6 +335,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
|
|||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
using iterator = OutputIt;
|
using iterator = OutputIt;
|
||||||
using format_arg = basic_format_arg<basic_printf_context>;
|
using format_arg = basic_format_arg<basic_printf_context>;
|
||||||
|
using parse_context_type = basic_printf_parse_context<Char>;
|
||||||
template <typename T> using formatter_type = printf_formatter<T>;
|
template <typename T> using formatter_type = printf_formatter<T>;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -331,7 +343,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
|
|||||||
|
|
||||||
OutputIt out_;
|
OutputIt out_;
|
||||||
basic_format_args<basic_printf_context> args_;
|
basic_format_args<basic_printf_context> args_;
|
||||||
basic_format_parse_context<Char> parse_ctx_;
|
parse_context_type parse_ctx_;
|
||||||
|
|
||||||
static void parse_flags(format_specs& specs, const Char*& it,
|
static void parse_flags(format_specs& specs, const Char*& it,
|
||||||
const Char* end);
|
const Char* end);
|
||||||
@ -362,7 +374,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
|
|||||||
|
|
||||||
format_arg arg(int id) const { return args_.get(id); }
|
format_arg arg(int id) const { return args_.get(id); }
|
||||||
|
|
||||||
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
|
parse_context_type& parse_context() { return parse_ctx_; }
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_error(const char* message) {
|
FMT_CONSTEXPR void on_error(const char* message) {
|
||||||
parse_ctx_.on_error(message);
|
parse_ctx_.on_error(message);
|
||||||
@ -471,7 +483,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||||||
|
|
||||||
// Parse argument index, flags and width.
|
// Parse argument index, flags and width.
|
||||||
int arg_index = parse_header(it, end, specs);
|
int arg_index = parse_header(it, end, specs);
|
||||||
if (arg_index == 0) on_error("argument index out of range");
|
if (arg_index == 0) on_error("argument not found");
|
||||||
|
|
||||||
// Parse precision.
|
// Parse precision.
|
||||||
if (it != end && *it == '.') {
|
if (it != end && *it == '.') {
|
||||||
@ -490,6 +502,13 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
format_arg arg = get_arg(arg_index);
|
format_arg arg = get_arg(arg_index);
|
||||||
|
if (specs.precision >= 0 && arg.type() == internal::type::cstring_type) {
|
||||||
|
auto str = visit_format_arg(internal::get_cstring<Char>(), arg);
|
||||||
|
auto str_end = str + specs.precision;
|
||||||
|
auto nul = std::find(str, str_end, Char());
|
||||||
|
arg = internal::make_arg<basic_printf_context>(basic_string_view<Char>(
|
||||||
|
str, nul != str_end ? nul - str : specs.precision));
|
||||||
|
}
|
||||||
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
|
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
|
||||||
specs.alt = false;
|
specs.alt = false;
|
||||||
if (specs.fill[0] == '0') {
|
if (specs.fill[0] == '0') {
|
||||||
@ -605,7 +624,7 @@ inline std::basic_string<Char> vsprintf(
|
|||||||
const S& format,
|
const S& format,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
printf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(format), args);
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,7 +649,7 @@ inline int vfprintf(
|
|||||||
std::FILE* f, const S& format,
|
std::FILE* f, const S& format,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
printf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(format), args);
|
||||||
std::size_t size = buffer.size();
|
std::size_t size = buffer.size();
|
||||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||||
? -1
|
? -1
|
||||||
@ -683,7 +702,7 @@ inline int vfprintf(
|
|||||||
std::basic_ostream<Char>& os, const S& format,
|
std::basic_ostream<Char>& os, const S& format,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
printf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(format), args);
|
||||||
internal::write(os, buffer);
|
internal::write(os, buffer);
|
||||||
return static_cast<int>(buffer.size());
|
return static_cast<int>(buffer.size());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,9 @@
|
|||||||
#ifndef FMT_RANGES_H_
|
#ifndef FMT_RANGES_H_
|
||||||
#define FMT_RANGES_H_
|
#define FMT_RANGES_H_
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
// output only up to N items from the range.
|
// output only up to N items from the range.
|
||||||
@ -104,10 +106,7 @@ struct is_range_<
|
|||||||
/// tuple_size and tuple_element check.
|
/// tuple_size and tuple_element check.
|
||||||
template <typename T> class is_tuple_like_ {
|
template <typename T> class is_tuple_like_ {
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static auto check(U* p)
|
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||||
-> decltype(std::tuple_size<U>::value,
|
|
||||||
(void)std::declval<typename std::tuple_element<0, U>::type>(),
|
|
||||||
int());
|
|
||||||
template <typename> static void check(...);
|
template <typename> static void check(...);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -360,6 +359,29 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
|||||||
return {tuple, sep};
|
return {tuple, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Returns an object that formats `initializer_list` with elements separated by
|
||||||
|
`sep`.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
|
||||||
|
// Output: "1, 2, 3"
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
|
||||||
|
std::initializer_list<T> list, string_view sep) {
|
||||||
|
return join(std::begin(list), std::end(list), sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
|
||||||
|
std::initializer_list<T> list, wstring_view sep) {
|
||||||
|
return join(std::begin(list), std::end(list), sep);
|
||||||
|
}
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_RANGES_H_
|
#endif // FMT_RANGES_H_
|
||||||
|
|||||||
@ -13,7 +13,7 @@ namespace internal {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||||
T value) {
|
T value) {
|
||||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
#ifdef FMT_FUZZ
|
||||||
if (precision > 100000)
|
if (precision > 100000)
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"fuzz mode - avoid large allocation inside snprintf");
|
"fuzz mode - avoid large allocation inside snprintf");
|
||||||
@ -29,7 +29,7 @@ struct sprintf_specs {
|
|||||||
bool alt : 1;
|
bool alt : 1;
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
constexpr sprintf_specs(basic_format_specs<Char> specs)
|
constexpr explicit sprintf_specs(basic_format_specs<Char> specs)
|
||||||
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
|
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
|
||||||
|
|
||||||
constexpr bool has_precision() const { return precision >= 0; }
|
constexpr bool has_precision() const { return precision >= 0; }
|
||||||
@ -81,7 +81,8 @@ char* sprintf_format(Double value, internal::buffer<char>& buf,
|
|||||||
unsigned n = internal::to_unsigned(result);
|
unsigned n = internal::to_unsigned(result);
|
||||||
if (n < buf.capacity()) {
|
if (n < buf.capacity()) {
|
||||||
// Find the decimal point.
|
// Find the decimal point.
|
||||||
auto p = buf.data(), end = p + n;
|
auto* p = buf.data();
|
||||||
|
auto* end = p + n;
|
||||||
if (*p == '+' || *p == '-') ++p;
|
if (*p == '+' || *p == '-') ++p;
|
||||||
if (specs.type != 'a' && specs.type != 'A') {
|
if (specs.type != 'a' && specs.type != 'A') {
|
||||||
while (p < end && *p >= '0' && *p <= '9') ++p;
|
while (p < end && *p >= '0' && *p <= '9') ++p;
|
||||||
@ -113,6 +114,46 @@ char* sprintf_format(Double value, internal::buffer<char>& buf,
|
|||||||
}
|
}
|
||||||
return decimal_point_pos;
|
return decimal_point_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEPRECATED.
|
||||||
|
template <typename Context> class arg_map {
|
||||||
|
private:
|
||||||
|
struct entry {
|
||||||
|
basic_string_view<typename Context::char_type> name;
|
||||||
|
basic_format_arg<Context> arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
entry* map_;
|
||||||
|
unsigned size_;
|
||||||
|
|
||||||
|
void push_back(value<Context> val) {
|
||||||
|
const auto& named = *val.named_arg;
|
||||||
|
map_[size_] = {named.name, named.template deserialize<Context>()};
|
||||||
|
++size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void init(const basic_format_args<Context>& args);
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is deprecated and is kept only to preserve ABI compatibility.
|
||||||
|
template <typename Context>
|
||||||
|
void arg_map<Context>::init(const basic_format_args<Context>& args) {
|
||||||
|
if (map_) return;
|
||||||
|
map_ = new entry[internal::to_unsigned(args.max_size())];
|
||||||
|
if (args.is_packed()) {
|
||||||
|
for (int i = 0;; ++i) {
|
||||||
|
internal::type arg_type = args.type(i);
|
||||||
|
if (arg_type == internal::type::none_type) return;
|
||||||
|
if (arg_type == internal::type::named_arg_type)
|
||||||
|
push_back(args.values_[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0, n = args.max_size(); i < n; ++i) {
|
||||||
|
auto type = args.args_[i].type_;
|
||||||
|
if (type == internal::type::named_arg_type) push_back(args.args_[i].value_);
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
|
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
|
||||||
@ -170,7 +211,4 @@ template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
|
|||||||
|
|
||||||
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
|
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
|
||||||
const wchar_t*);
|
const wchar_t*);
|
||||||
|
|
||||||
template FMT_API std::wstring internal::vformat<wchar_t>(
|
|
||||||
wstring_view, basic_format_args<wformat_context>);
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|||||||
26
support/cmake/JoinPaths.cmake
Normal file
26
support/cmake/JoinPaths.cmake
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# This module provides function for joining paths
|
||||||
|
# known from from most languages
|
||||||
|
#
|
||||||
|
# Original license:
|
||||||
|
# SPDX-License-Identifier: (MIT OR CC0-1.0)
|
||||||
|
# Explicit permission given to distribute this module under
|
||||||
|
# the terms of the project as described in /LICENSE.rst.
|
||||||
|
# Copyright 2020 Jan Tojnar
|
||||||
|
# https://github.com/jtojnar/cmake-snips
|
||||||
|
#
|
||||||
|
# Modelled after Python’s os.path.join
|
||||||
|
# https://docs.python.org/3.7/library/os.path.html#os.path.join
|
||||||
|
# Windows not supported
|
||||||
|
function(join_paths joined_path first_path_segment)
|
||||||
|
set(temp_path "${first_path_segment}")
|
||||||
|
foreach(current_segment IN LISTS ARGN)
|
||||||
|
if(NOT ("${current_segment}" STREQUAL ""))
|
||||||
|
if(IS_ABSOLUTE "${current_segment}")
|
||||||
|
set(temp_path "${current_segment}")
|
||||||
|
else()
|
||||||
|
set(temp_path "${temp_path}/${current_segment}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
set(${joined_path} "${temp_path}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
@ -1,7 +1,7 @@
|
|||||||
prefix=@CMAKE_INSTALL_PREFIX@
|
prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
|
libdir=@libdir_for_pc_file@
|
||||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
includedir=@includedir_for_pc_file@
|
||||||
|
|
||||||
Name: fmt
|
Name: fmt
|
||||||
Description: A modern formatting library
|
Description: A modern formatting library
|
||||||
|
|||||||
@ -144,9 +144,15 @@ def update_site(env):
|
|||||||
b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data)
|
b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data)
|
||||||
b.data = b.data.replace('std::FILE*', 'std::FILE *')
|
b.data = b.data.replace('std::FILE*', 'std::FILE *')
|
||||||
b.data = b.data.replace('unsigned int', 'unsigned')
|
b.data = b.data.replace('unsigned int', 'unsigned')
|
||||||
b.data = b.data.replace('operator""_', 'operator"" _')
|
#b.data = b.data.replace('operator""_', 'operator"" _')
|
||||||
b.data = b.data.replace(', size_t', ', std::size_t')
|
b.data = b.data.replace(', size_t', ', std::size_t')
|
||||||
b.data = b.data.replace('aa long', 'a long')
|
b.data = b.data.replace('aa long', 'a long')
|
||||||
|
if version == '6.2.0':
|
||||||
|
b.data = b.data.replace(
|
||||||
|
'vformat(const S&, basic_format_args<' +
|
||||||
|
'buffer_context<Char>>)',
|
||||||
|
'vformat(const S&, basic_format_args<' +
|
||||||
|
'buffer_context<type_identity_t<Char>>>)')
|
||||||
# Fix a broken link in index.rst.
|
# Fix a broken link in index.rst.
|
||||||
index = os.path.join(target_doc_dir, 'index.rst')
|
index = os.path.join(target_doc_dir, 'index.rst')
|
||||||
with rewrite(index) as b:
|
with rewrite(index) as b:
|
||||||
|
|||||||
@ -95,7 +95,6 @@ add_fmt_test(grisu-test)
|
|||||||
target_compile_definitions(grisu-test PRIVATE FMT_USE_GRISU=1)
|
target_compile_definitions(grisu-test PRIVATE FMT_USE_GRISU=1)
|
||||||
add_fmt_test(gtest-extra-test)
|
add_fmt_test(gtest-extra-test)
|
||||||
add_fmt_test(format-test mock-allocator.h)
|
add_fmt_test(format-test mock-allocator.h)
|
||||||
add_fmt_test(format-dyn-args-test)
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
target_compile_options(format-test PRIVATE /bigobj)
|
target_compile_options(format-test PRIVATE /bigobj)
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
@ -50,6 +50,8 @@ TEST(ColorsTest, ColorsPrint) {
|
|||||||
TEST(ColorsTest, Format) {
|
TEST(ColorsTest, Format) {
|
||||||
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
|
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
|
||||||
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
|
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
|
||||||
|
EXPECT_EQ(fmt::format(fg(fmt::rgb(255, 20, 30)), L"rgb(255,20,30) wide"),
|
||||||
|
L"\x1b[38;2;255;020;030mrgb(255,20,30) wide\x1b[0m");
|
||||||
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"),
|
EXPECT_EQ(fmt::format(fg(fmt::color::blue), "blue"),
|
||||||
"\x1b[38;2;000;000;255mblue\x1b[0m");
|
"\x1b[38;2;000;000;255mblue\x1b[0m");
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
|
|||||||
@ -15,9 +15,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "test-assert.h"
|
|
||||||
|
|
||||||
#include "gmock.h"
|
#include "gmock.h"
|
||||||
|
#include "test-assert.h"
|
||||||
|
|
||||||
// Check if fmt/core.h compiles with windows.h included before it.
|
// Check if fmt/core.h compiles with windows.h included before it.
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -211,7 +210,8 @@ TEST(ArgTest, FormatArgs) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct custom_context {
|
struct custom_context {
|
||||||
typedef char char_type;
|
using char_type = char;
|
||||||
|
using parse_context_type = fmt::format_parse_context;
|
||||||
|
|
||||||
template <typename T> struct formatter_type {
|
template <typename T> struct formatter_type {
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
@ -402,6 +402,86 @@ TEST(ArgTest, VisitInvalidArg) {
|
|||||||
fmt::visit_format_arg(visitor, arg);
|
fmt::visit_format_arg(visitor, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(FormatDynArgsTest, Basic) {
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(FormatDynArgsTest, StringsAndRefs) {
|
||||||
|
// Unfortunately the tests are compiled with old ABI so strings use COW.
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
char str[] = "1234567890";
|
||||||
|
store.push_back(str);
|
||||||
|
store.push_back(std::cref(str));
|
||||||
|
store.push_back(fmt::string_view{str});
|
||||||
|
str[0] = 'X';
|
||||||
|
|
||||||
|
std::string result = fmt::vformat("{} and {} and {}", store);
|
||||||
|
EXPECT_EQ("1234567890 and X234567890 and X234567890", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct custom_type {
|
||||||
|
int i = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
template <> struct formatter<custom_type> {
|
||||||
|
auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const custom_type& p, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
return format_to(ctx.out(), "cust={}", p.i);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
TEST(FormatDynArgsTest, CustomFormat) {
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
custom_type c{};
|
||||||
|
store.push_back(c);
|
||||||
|
++c.i;
|
||||||
|
store.push_back(c);
|
||||||
|
++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);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct copy_throwable {
|
||||||
|
copy_throwable() {}
|
||||||
|
copy_throwable(const copy_throwable&) { throw "deal with it"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
template <> struct formatter<copy_throwable> {
|
||||||
|
auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
auto format(copy_throwable, format_context& ctx) -> decltype(ctx.out()) {
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
TEST(FormatDynArgsTest, ThrowOnCopy) {
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
store.push_back(std::string("foo"));
|
||||||
|
try {
|
||||||
|
store.push_back(copy_throwable());
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
EXPECT_EQ(fmt::vformat("{}", store), "foo");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(StringViewTest, ValueType) {
|
TEST(StringViewTest, ValueType) {
|
||||||
static_assert(std::is_same<string_view::value_type, char>::value, "");
|
static_assert(std::is_same<string_view::value_type, char>::value, "");
|
||||||
}
|
}
|
||||||
@ -520,23 +600,6 @@ inline fmt::basic_string_view<Char> to_string_view(const my_string<Char>& s)
|
|||||||
struct non_string {};
|
struct non_string {};
|
||||||
} // namespace my_ns
|
} // namespace my_ns
|
||||||
|
|
||||||
namespace FakeQt {
|
|
||||||
class QString {
|
|
||||||
public:
|
|
||||||
QString(const wchar_t* s) : s_(std::make_shared<std::wstring>(s)) {}
|
|
||||||
const wchar_t* utf16() const FMT_NOEXCEPT { return s_->data(); }
|
|
||||||
int size() const FMT_NOEXCEPT { return static_cast<int>(s_->size()); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<std::wstring> s_;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline fmt::basic_string_view<wchar_t> to_string_view(const QString& s)
|
|
||||||
FMT_NOEXCEPT {
|
|
||||||
return {s.utf16(), static_cast<std::size_t>(s.size())};
|
|
||||||
}
|
|
||||||
} // namespace FakeQt
|
|
||||||
|
|
||||||
template <typename T> class IsStringTest : public testing::Test {};
|
template <typename T> class IsStringTest : public testing::Test {};
|
||||||
|
|
||||||
typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes;
|
typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes;
|
||||||
@ -562,7 +625,6 @@ TYPED_TEST(IsStringTest, IsString) {
|
|||||||
fmt::internal::is_string<string_view>::value);
|
fmt::internal::is_string<string_view>::value);
|
||||||
EXPECT_TRUE(fmt::internal::is_string<my_ns::my_string<TypeParam>>::value);
|
EXPECT_TRUE(fmt::internal::is_string<my_ns::my_string<TypeParam>>::value);
|
||||||
EXPECT_FALSE(fmt::internal::is_string<my_ns::non_string>::value);
|
EXPECT_FALSE(fmt::internal::is_string<my_ns::non_string>::value);
|
||||||
EXPECT_TRUE(fmt::internal::is_string<FakeQt::QString>::value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CoreTest, Format) {
|
TEST(CoreTest, Format) {
|
||||||
@ -585,33 +647,16 @@ TEST(CoreTest, FormatTo) {
|
|||||||
|
|
||||||
TEST(CoreTest, ToStringViewForeignStrings) {
|
TEST(CoreTest, ToStringViewForeignStrings) {
|
||||||
using namespace my_ns;
|
using namespace my_ns;
|
||||||
using namespace FakeQt;
|
|
||||||
EXPECT_EQ(to_string_view(my_string<char>("42")), "42");
|
EXPECT_EQ(to_string_view(my_string<char>("42")), "42");
|
||||||
EXPECT_EQ(to_string_view(my_string<wchar_t>(L"42")), L"42");
|
|
||||||
EXPECT_EQ(to_string_view(QString(L"42")), L"42");
|
|
||||||
fmt::internal::type type =
|
fmt::internal::type type =
|
||||||
fmt::internal::mapped_type_constant<my_string<char>,
|
fmt::internal::mapped_type_constant<my_string<char>,
|
||||||
fmt::format_context>::value;
|
fmt::format_context>::value;
|
||||||
EXPECT_EQ(type, fmt::internal::type::string_type);
|
EXPECT_EQ(type, fmt::internal::type::string_type);
|
||||||
type = fmt::internal::mapped_type_constant<my_string<wchar_t>,
|
|
||||||
fmt::wformat_context>::value;
|
|
||||||
EXPECT_EQ(type, fmt::internal::type::string_type);
|
|
||||||
type =
|
|
||||||
fmt::internal::mapped_type_constant<QString, fmt::wformat_context>::value;
|
|
||||||
EXPECT_EQ(type, fmt::internal::type::string_type);
|
|
||||||
// Does not compile: only wide format contexts are compatible with QString!
|
|
||||||
// type = fmt::internal::mapped_type_constant<QString,
|
|
||||||
// fmt::format_context>::value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CoreTest, FormatForeignStrings) {
|
TEST(CoreTest, FormatForeignStrings) {
|
||||||
using namespace my_ns;
|
using namespace my_ns;
|
||||||
using namespace FakeQt;
|
|
||||||
EXPECT_EQ(fmt::format(my_string<char>("{}"), 42), "42");
|
EXPECT_EQ(fmt::format(my_string<char>("{}"), 42), "42");
|
||||||
EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), 42), L"42");
|
|
||||||
EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
|
|
||||||
EXPECT_EQ(fmt::format(QString(L"{}"), my_string<wchar_t>(L"42")), L"42");
|
|
||||||
EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), QString(L"42")), L"42");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct implicitly_convertible_to_string {
|
struct implicitly_convertible_to_string {
|
||||||
@ -646,15 +691,6 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
|
|||||||
fmt::format("{}", explicitly_convertible_to_std_string_view()));
|
fmt::format("{}", explicitly_convertible_to_std_string_view()));
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
struct explicitly_convertible_to_wstring_view {
|
|
||||||
explicit operator fmt::wstring_view() const { return L"foo"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
|
|
||||||
EXPECT_EQ(L"foo",
|
|
||||||
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct disabled_rvalue_conversion {
|
struct disabled_rvalue_conversion {
|
||||||
|
|||||||
@ -4,84 +4,3 @@
|
|||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
|
|
||||||
TEST(FormatDynArgsTest, Basic) {
|
|
||||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(FormatDynArgsTest, StringsAndRefs) {
|
|
||||||
// Unfortunately the tests are compiled with old ABI
|
|
||||||
// So strings use COW.
|
|
||||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
|
||||||
char str[]{"1234567890"};
|
|
||||||
store.push_back(str);
|
|
||||||
store.push_back(std::cref(str));
|
|
||||||
store.push_back(fmt::string_view{str});
|
|
||||||
str[0] = 'X';
|
|
||||||
|
|
||||||
std::string result = fmt::vformat("{} and {} and {}", store);
|
|
||||||
|
|
||||||
EXPECT_EQ("1234567890 and X234567890 and X234567890", result);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct custom_type {
|
|
||||||
int i{0};
|
|
||||||
};
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
template <> struct formatter<custom_type> {
|
|
||||||
auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(const custom_type& p, FormatContext& ctx) -> decltype(format_to(
|
|
||||||
ctx.out(), std::declval<typename FormatContext::char_type const*>())) {
|
|
||||||
return format_to(ctx.out(), "cust={}", p.i);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
TEST(FormatDynArgsTest, CustomFormat) {
|
|
||||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
|
||||||
custom_type c{};
|
|
||||||
store.push_back(c);
|
|
||||||
++c.i;
|
|
||||||
store.push_back(c);
|
|
||||||
++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);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(FormatDynArgsTest, NamedArgByRef) {
|
|
||||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
|
||||||
|
|
||||||
// Note: fmt::arg() constructs an object which holds a reference
|
|
||||||
// to its value. It's not an aggregate, so it doesn't extend the
|
|
||||||
// reference lifetime. As a result, it's a very bad idea passing temporary
|
|
||||||
// as a named argument value. Only GCC with optimization level >0
|
|
||||||
// complains about this.
|
|
||||||
//
|
|
||||||
// A real life usecase is when you have both name and value alive
|
|
||||||
// guarantee their lifetime and thus don't want them to be copied into
|
|
||||||
// storages.
|
|
||||||
int a1_val{42};
|
|
||||||
auto a1 = fmt::arg("a1_", a1_val);
|
|
||||||
store.push_back(std::cref(a1));
|
|
||||||
|
|
||||||
std::string result = fmt::vformat("{a1_}", // and {} and {}",
|
|
||||||
store);
|
|
||||||
|
|
||||||
EXPECT_EQ("42", result);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -10,12 +10,11 @@
|
|||||||
#include "test-assert.h"
|
#include "test-assert.h"
|
||||||
|
|
||||||
// Include format.cc instead of format.h to test implementation.
|
// Include format.cc instead of format.h to test implementation.
|
||||||
#include "../src/format.cc"
|
|
||||||
#include "fmt/printf.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "../src/format.cc"
|
||||||
|
#include "fmt/printf.h"
|
||||||
#include "gmock.h"
|
#include "gmock.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -430,8 +429,10 @@ TEST(FormatTest, CountCodePoints) {
|
|||||||
#ifndef __cpp_char8_t
|
#ifndef __cpp_char8_t
|
||||||
using fmt::char8_t;
|
using fmt::char8_t;
|
||||||
#endif
|
#endif
|
||||||
EXPECT_EQ(4, fmt::internal::count_code_points(
|
EXPECT_EQ(
|
||||||
fmt::basic_string_view<char8_t>(reinterpret_cast<const char8_t*>("ёжик"))));
|
4, fmt::internal::count_code_points(
|
||||||
|
fmt::basic_string_view<fmt::internal::char8_type>(
|
||||||
|
reinterpret_cast<const fmt::internal::char8_type*>("ёжик"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests fmt::internal::count_digits for integer type Int.
|
// Tests fmt::internal::count_digits for integer type Int.
|
||||||
|
|||||||
@ -46,6 +46,7 @@ using fmt::format_error;
|
|||||||
using fmt::memory_buffer;
|
using fmt::memory_buffer;
|
||||||
using fmt::string_view;
|
using fmt::string_view;
|
||||||
using fmt::wmemory_buffer;
|
using fmt::wmemory_buffer;
|
||||||
|
using fmt::wstring_view;
|
||||||
using fmt::internal::basic_writer;
|
using fmt::internal::basic_writer;
|
||||||
using fmt::internal::max_value;
|
using fmt::internal::max_value;
|
||||||
|
|
||||||
@ -639,15 +640,14 @@ TEST(FormatterTest, ArgErrors) {
|
|||||||
EXPECT_THROW_MSG(format("{"), format_error, "invalid format string");
|
EXPECT_THROW_MSG(format("{"), format_error, "invalid format string");
|
||||||
EXPECT_THROW_MSG(format("{?}"), format_error, "invalid format string");
|
EXPECT_THROW_MSG(format("{?}"), format_error, "invalid format string");
|
||||||
EXPECT_THROW_MSG(format("{0"), format_error, "invalid format string");
|
EXPECT_THROW_MSG(format("{0"), format_error, "invalid format string");
|
||||||
EXPECT_THROW_MSG(format("{0}"), format_error, "argument index out of range");
|
EXPECT_THROW_MSG(format("{0}"), format_error, "argument not found");
|
||||||
EXPECT_THROW_MSG(format("{00}", 42), format_error, "invalid format string");
|
EXPECT_THROW_MSG(format("{00}", 42), format_error, "invalid format string");
|
||||||
|
|
||||||
char format_str[BUFFER_SIZE];
|
char format_str[BUFFER_SIZE];
|
||||||
safe_sprintf(format_str, "{%u", INT_MAX);
|
safe_sprintf(format_str, "{%u", INT_MAX);
|
||||||
EXPECT_THROW_MSG(format(format_str), format_error, "invalid format string");
|
EXPECT_THROW_MSG(format(format_str), format_error, "invalid format string");
|
||||||
safe_sprintf(format_str, "{%u}", INT_MAX);
|
safe_sprintf(format_str, "{%u}", INT_MAX);
|
||||||
EXPECT_THROW_MSG(format(format_str), format_error,
|
EXPECT_THROW_MSG(format(format_str), format_error, "argument not found");
|
||||||
"argument index out of range");
|
|
||||||
|
|
||||||
safe_sprintf(format_str, "{%u", INT_MAX + 1u);
|
safe_sprintf(format_str, "{%u", INT_MAX + 1u);
|
||||||
EXPECT_THROW_MSG(format(format_str), format_error, "number is too big");
|
EXPECT_THROW_MSG(format(format_str), format_error, "number is too big");
|
||||||
@ -672,13 +672,13 @@ template <> struct TestFormat<0> {
|
|||||||
TEST(FormatterTest, ManyArgs) {
|
TEST(FormatterTest, ManyArgs) {
|
||||||
EXPECT_EQ("19", TestFormat<20>::format("{19}"));
|
EXPECT_EQ("19", TestFormat<20>::format("{19}"));
|
||||||
EXPECT_THROW_MSG(TestFormat<20>::format("{20}"), format_error,
|
EXPECT_THROW_MSG(TestFormat<20>::format("{20}"), format_error,
|
||||||
"argument index out of range");
|
"argument not found");
|
||||||
EXPECT_THROW_MSG(TestFormat<21>::format("{21}"), format_error,
|
EXPECT_THROW_MSG(TestFormat<21>::format("{21}"), format_error,
|
||||||
"argument index out of range");
|
"argument not found");
|
||||||
enum { max_packed_args = fmt::internal::max_packed_args };
|
enum { max_packed_args = fmt::internal::max_packed_args };
|
||||||
std::string format_str = fmt::format("{{{}}}", max_packed_args + 1);
|
std::string format_str = fmt::format("{{{}}}", max_packed_args + 1);
|
||||||
EXPECT_THROW_MSG(TestFormat<max_packed_args>::format(format_str),
|
EXPECT_THROW_MSG(TestFormat<max_packed_args>::format(format_str),
|
||||||
format_error, "argument index out of range");
|
format_error, "argument not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, NamedArg) {
|
TEST(FormatterTest, NamedArg) {
|
||||||
@ -707,7 +707,7 @@ TEST(FormatterTest, AutoArgIndex) {
|
|||||||
"cannot switch from manual to automatic argument indexing");
|
"cannot switch from manual to automatic argument indexing");
|
||||||
EXPECT_THROW_MSG(format("{:.{0}}", 1.2345, 2), format_error,
|
EXPECT_THROW_MSG(format("{:.{0}}", 1.2345, 2), format_error,
|
||||||
"cannot switch from automatic to manual argument indexing");
|
"cannot switch from automatic to manual argument indexing");
|
||||||
EXPECT_THROW_MSG(format("{}"), format_error, "argument index out of range");
|
EXPECT_THROW_MSG(format("{}"), format_error, "argument not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, EmptySpecs) { EXPECT_EQ("42", format("{0:}", 42)); }
|
TEST(FormatterTest, EmptySpecs) { EXPECT_EQ("42", format("{0:}", 42)); }
|
||||||
@ -1010,8 +1010,7 @@ TEST(FormatterTest, RuntimeWidth) {
|
|||||||
EXPECT_THROW_MSG(format("{0:{}", 0), format_error,
|
EXPECT_THROW_MSG(format("{0:{}", 0), format_error,
|
||||||
"cannot switch from manual to automatic argument indexing");
|
"cannot switch from manual to automatic argument indexing");
|
||||||
EXPECT_THROW_MSG(format("{0:{?}}", 0), format_error, "invalid format string");
|
EXPECT_THROW_MSG(format("{0:{?}}", 0), format_error, "invalid format string");
|
||||||
EXPECT_THROW_MSG(format("{0:{1}}", 0), format_error,
|
EXPECT_THROW_MSG(format("{0:{1}}", 0), format_error, "argument not found");
|
||||||
"argument index out of range");
|
|
||||||
|
|
||||||
EXPECT_THROW_MSG(format("{0:{0:}}", 0), format_error,
|
EXPECT_THROW_MSG(format("{0:{0:}}", 0), format_error,
|
||||||
"invalid format string");
|
"invalid format string");
|
||||||
@ -1124,6 +1123,8 @@ TEST(FormatterTest, Precision) {
|
|||||||
"000000000000000000000000000000000000000000000000000P+127",
|
"000000000000000000000000000000000000000000000000000P+127",
|
||||||
format("{:.838A}", -2.14001164E+38));
|
format("{:.838A}", -2.14001164E+38));
|
||||||
EXPECT_EQ("123.", format("{:#.0f}", 123.0));
|
EXPECT_EQ("123.", format("{:#.0f}", 123.0));
|
||||||
|
EXPECT_EQ("1.23", format("{:.02f}", 1.234));
|
||||||
|
EXPECT_EQ("0.001", format("{:.1g}", 0.001));
|
||||||
|
|
||||||
EXPECT_THROW_MSG(format("{0:.2}", reinterpret_cast<void*>(0xcafe)),
|
EXPECT_THROW_MSG(format("{0:.2}", reinterpret_cast<void*>(0xcafe)),
|
||||||
format_error,
|
format_error,
|
||||||
@ -1157,8 +1158,7 @@ TEST(FormatterTest, RuntimePrecision) {
|
|||||||
"invalid format string");
|
"invalid format string");
|
||||||
EXPECT_THROW_MSG(format("{0:.{1}", 0, 0), format_error,
|
EXPECT_THROW_MSG(format("{0:.{1}", 0, 0), format_error,
|
||||||
"precision not allowed for this argument type");
|
"precision not allowed for this argument type");
|
||||||
EXPECT_THROW_MSG(format("{0:.{1}}", 0), format_error,
|
EXPECT_THROW_MSG(format("{0:.{1}}", 0), format_error, "argument not found");
|
||||||
"argument index out of range");
|
|
||||||
|
|
||||||
EXPECT_THROW_MSG(format("{0:.{0:}}", 0), format_error,
|
EXPECT_THROW_MSG(format("{0:.{0:}}", 0), format_error,
|
||||||
"invalid format string");
|
"invalid format string");
|
||||||
@ -1253,7 +1253,7 @@ TEST(FormatterTest, FormatShort) {
|
|||||||
TEST(FormatterTest, FormatInt) {
|
TEST(FormatterTest, FormatInt) {
|
||||||
EXPECT_THROW_MSG(format("{0:v", 42), format_error,
|
EXPECT_THROW_MSG(format("{0:v", 42), format_error,
|
||||||
"missing '}' in format string");
|
"missing '}' in format string");
|
||||||
check_unknown_types(42, "bBdoxXn", "integer");
|
check_unknown_types(42, "bBdoxXnL", "integer");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, FormatBin) {
|
TEST(FormatterTest, FormatBin) {
|
||||||
@ -1394,6 +1394,7 @@ TEST(FormatterTest, FormatOct) {
|
|||||||
|
|
||||||
TEST(FormatterTest, FormatIntLocale) {
|
TEST(FormatterTest, FormatIntLocale) {
|
||||||
EXPECT_EQ("1234", format("{:n}", 1234));
|
EXPECT_EQ("1234", format("{:n}", 1234));
|
||||||
|
EXPECT_EQ("1234", format("{:L}", 1234));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ConvertibleToLongLong {
|
struct ConvertibleToLongLong {
|
||||||
@ -1409,7 +1410,7 @@ TEST(FormatterTest, FormatFloat) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, FormatDouble) {
|
TEST(FormatterTest, FormatDouble) {
|
||||||
check_unknown_types(1.2, "eEfFgGaAn%", "double");
|
check_unknown_types(1.2, "eEfFgGaAnL%", "double");
|
||||||
EXPECT_EQ("0.0", format("{:}", 0.0));
|
EXPECT_EQ("0.0", format("{:}", 0.0));
|
||||||
EXPECT_EQ("0.000000", format("{:f}", 0.0));
|
EXPECT_EQ("0.000000", format("{:f}", 0.0));
|
||||||
EXPECT_EQ("0", format("{:g}", 0.0));
|
EXPECT_EQ("0", format("{:g}", 0.0));
|
||||||
@ -1418,6 +1419,7 @@ TEST(FormatterTest, FormatDouble) {
|
|||||||
EXPECT_EQ("392.65", format("{:G}", 392.65));
|
EXPECT_EQ("392.65", format("{:G}", 392.65));
|
||||||
EXPECT_EQ("392.650000", format("{:f}", 392.65));
|
EXPECT_EQ("392.650000", format("{:f}", 392.65));
|
||||||
EXPECT_EQ("392.650000", format("{:F}", 392.65));
|
EXPECT_EQ("392.650000", format("{:F}", 392.65));
|
||||||
|
EXPECT_EQ("42", format("{:L}", 42.0));
|
||||||
char buffer[BUFFER_SIZE];
|
char buffer[BUFFER_SIZE];
|
||||||
safe_sprintf(buffer, "%e", 392.65);
|
safe_sprintf(buffer, "%e", 392.65);
|
||||||
EXPECT_EQ(buffer, format("{0:e}", 392.65));
|
EXPECT_EQ(buffer, format("{0:e}", 392.65));
|
||||||
@ -1488,7 +1490,7 @@ TEST(FormatterTest, FormatLongDouble) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, FormatChar) {
|
TEST(FormatterTest, FormatChar) {
|
||||||
const char types[] = "cbBdoxXn";
|
const char types[] = "cbBdoxXnL";
|
||||||
check_unknown_types('a', types, "char");
|
check_unknown_types('a', types, "char");
|
||||||
EXPECT_EQ("a", format("{0}", 'a'));
|
EXPECT_EQ("a", format("{0}", 'a'));
|
||||||
EXPECT_EQ("z", format("{0:c}", 'z'));
|
EXPECT_EQ("z", format("{0:c}", 'z'));
|
||||||
@ -1604,6 +1606,40 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// std::is_constructible is broken in MSVC until version 2015.
|
||||||
|
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
|
||||||
|
struct explicitly_convertible_to_wstring_view {
|
||||||
|
explicit operator fmt::wstring_view() const { return L"foo"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(FormatTest, FormatExplicitlyConvertibleToWStringView) {
|
||||||
|
EXPECT_EQ(L"foo",
|
||||||
|
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace fake_qt {
|
||||||
|
class QString {
|
||||||
|
public:
|
||||||
|
QString(const wchar_t* s) : s_(std::make_shared<std::wstring>(s)) {}
|
||||||
|
const wchar_t* utf16() const FMT_NOEXCEPT { return s_->data(); }
|
||||||
|
int size() const FMT_NOEXCEPT { return static_cast<int>(s_->size()); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<std::wstring> s_;
|
||||||
|
};
|
||||||
|
|
||||||
|
fmt::basic_string_view<wchar_t> to_string_view(const QString& s) FMT_NOEXCEPT {
|
||||||
|
return {s.utf16(), static_cast<std::size_t>(s.size())};
|
||||||
|
}
|
||||||
|
} // namespace fake_qt
|
||||||
|
|
||||||
|
TEST(FormatTest, FormatForeignStrings) {
|
||||||
|
using fake_qt::QString;
|
||||||
|
EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
|
||||||
|
EXPECT_EQ(fmt::format(QString(L"{}"), QString(L"42")), L"42");
|
||||||
|
}
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
template <> struct formatter<Date> {
|
template <> struct formatter<Date> {
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
@ -1754,6 +1790,8 @@ TEST(FormatTest, Print) {
|
|||||||
EXPECT_WRITE(stderr, fmt::print(stderr, "Don't {}!", "panic"),
|
EXPECT_WRITE(stderr, fmt::print(stderr, "Don't {}!", "panic"),
|
||||||
"Don't panic!");
|
"Don't panic!");
|
||||||
#endif
|
#endif
|
||||||
|
// Check that the wide print overload compiles.
|
||||||
|
if (fmt::internal::const_check(false)) fmt::print(L"test");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatTest, Variadic) {
|
TEST(FormatTest, Variadic) {
|
||||||
@ -1852,10 +1890,23 @@ TEST(FormatTest, UnpackedArgs) {
|
|||||||
struct string_like {};
|
struct string_like {};
|
||||||
fmt::string_view to_string_view(string_like) { return "foo"; }
|
fmt::string_view to_string_view(string_like) { return "foo"; }
|
||||||
|
|
||||||
|
constexpr char with_null[3] = {'{', '}', '\0'};
|
||||||
|
constexpr char no_null[2] = {'{', '}'};
|
||||||
|
|
||||||
TEST(FormatTest, CompileTimeString) {
|
TEST(FormatTest, CompileTimeString) {
|
||||||
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
|
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
|
||||||
EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42));
|
EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42));
|
||||||
EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like()));
|
EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like()));
|
||||||
|
(void)with_null;
|
||||||
|
(void)no_null;
|
||||||
|
#if __cplusplus >= 201703L
|
||||||
|
EXPECT_EQ("42", fmt::format(FMT_STRING(with_null), 42));
|
||||||
|
EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42));
|
||||||
|
#endif
|
||||||
|
#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L
|
||||||
|
EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42));
|
||||||
|
EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatTest, CustomFormatCompileTimeString) {
|
TEST(FormatTest, CustomFormatCompileTimeString) {
|
||||||
@ -2240,8 +2291,9 @@ struct test_parse_context {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct test_context {
|
struct test_context {
|
||||||
typedef char char_type;
|
using char_type = char;
|
||||||
typedef fmt::basic_format_arg<test_context> format_arg;
|
using format_arg = fmt::basic_format_arg<test_context>;
|
||||||
|
using parse_context_type = fmt::format_parse_context;
|
||||||
|
|
||||||
template <typename T> struct formatter_type {
|
template <typename T> struct formatter_type {
|
||||||
typedef fmt::formatter<T, char_type> type;
|
typedef fmt::formatter<T, char_type> type;
|
||||||
@ -2401,8 +2453,10 @@ FMT_CONSTEXPR bool equal(const char* s1, const char* s2) {
|
|||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
FMT_CONSTEXPR bool test_error(const char* fmt, const char* expected_error) {
|
FMT_CONSTEXPR bool test_error(const char* fmt, const char* expected_error) {
|
||||||
const char* actual_error = nullptr;
|
const char* actual_error = nullptr;
|
||||||
fmt::internal::do_check_format_string<char, test_error_handler, Args...>(
|
string_view s(fmt, len(fmt));
|
||||||
string_view(fmt, len(fmt)), test_error_handler(actual_error));
|
fmt::internal::format_string_checker<char, test_error_handler, Args...>
|
||||||
|
checker(s, test_error_handler(actual_error));
|
||||||
|
fmt::internal::parse_format_string<true>(s, checker);
|
||||||
return equal(actual_error, expected_error);
|
return equal(actual_error, expected_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2415,12 +2469,12 @@ TEST(FormatTest, FormatStringErrors) {
|
|||||||
EXPECT_ERROR_NOARGS("foo", nullptr);
|
EXPECT_ERROR_NOARGS("foo", nullptr);
|
||||||
EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string");
|
EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string");
|
||||||
EXPECT_ERROR("{0:s", "unknown format specifier", Date);
|
EXPECT_ERROR("{0:s", "unknown format specifier", Date);
|
||||||
# if FMT_MSC_VER >= 1916
|
# if !FMT_MSC_VER || FMT_MSC_VER >= 1916
|
||||||
// This causes an internal compiler error in MSVC2017.
|
// This causes an internal compiler error in MSVC2017.
|
||||||
EXPECT_ERROR("{:{<}", "invalid fill character '{'", int);
|
EXPECT_ERROR("{:{<}", "invalid fill character '{'", int);
|
||||||
EXPECT_ERROR("{:10000000000}", "number is too big", int);
|
EXPECT_ERROR("{:10000000000}", "number is too big", int);
|
||||||
EXPECT_ERROR("{:.10000000000}", "number is too big", int);
|
EXPECT_ERROR("{:.10000000000}", "number is too big", int);
|
||||||
EXPECT_ERROR_NOARGS("{:x}", "argument index out of range");
|
EXPECT_ERROR_NOARGS("{:x}", "argument not found");
|
||||||
# if FMT_NUMERIC_ALIGN
|
# if FMT_NUMERIC_ALIGN
|
||||||
EXPECT_ERROR("{0:=5", "unknown format specifier", int);
|
EXPECT_ERROR("{0:=5", "unknown format specifier", int);
|
||||||
EXPECT_ERROR("{:=}", "format specifier requires numeric argument",
|
EXPECT_ERROR("{:=}", "format specifier requires numeric argument",
|
||||||
@ -2439,6 +2493,8 @@ TEST(FormatTest, FormatStringErrors) {
|
|||||||
EXPECT_ERROR("{:+}", "format specifier requires signed argument", unsigned);
|
EXPECT_ERROR("{:+}", "format specifier requires signed argument", unsigned);
|
||||||
EXPECT_ERROR("{:-}", "format specifier requires signed argument", unsigned);
|
EXPECT_ERROR("{:-}", "format specifier requires signed argument", unsigned);
|
||||||
EXPECT_ERROR("{: }", "format specifier requires signed argument", unsigned);
|
EXPECT_ERROR("{: }", "format specifier requires signed argument", unsigned);
|
||||||
|
EXPECT_ERROR("{:{}}", "argument not found", int);
|
||||||
|
EXPECT_ERROR("{:.{}}", "argument not found", double);
|
||||||
EXPECT_ERROR("{:.2}", "precision not allowed for this argument type", int);
|
EXPECT_ERROR("{:.2}", "precision not allowed for this argument type", int);
|
||||||
EXPECT_ERROR("{:s}", "invalid type specifier", int);
|
EXPECT_ERROR("{:s}", "invalid type specifier", int);
|
||||||
EXPECT_ERROR("{:s}", "invalid type specifier", bool);
|
EXPECT_ERROR("{:s}", "invalid type specifier", bool);
|
||||||
@ -2461,8 +2517,8 @@ TEST(FormatTest, FormatStringErrors) {
|
|||||||
EXPECT_ERROR("{:.{0x}}", "invalid format string", int);
|
EXPECT_ERROR("{:.{0x}}", "invalid format string", int);
|
||||||
EXPECT_ERROR("{:.{-}}", "invalid format string", int);
|
EXPECT_ERROR("{:.{-}}", "invalid format string", int);
|
||||||
EXPECT_ERROR("{:.x}", "missing precision specifier", int);
|
EXPECT_ERROR("{:.x}", "missing precision specifier", int);
|
||||||
EXPECT_ERROR_NOARGS("{}", "argument index out of range");
|
EXPECT_ERROR_NOARGS("{}", "argument not found");
|
||||||
EXPECT_ERROR("{1}", "argument index out of range", int);
|
EXPECT_ERROR("{1}", "argument not found", int);
|
||||||
EXPECT_ERROR("{1}{}",
|
EXPECT_ERROR("{1}{}",
|
||||||
"cannot switch from manual to automatic argument indexing", int,
|
"cannot switch from manual to automatic argument indexing", int,
|
||||||
int);
|
int);
|
||||||
@ -2504,25 +2560,6 @@ TEST(FormatTest, FmtStringInTemplate) {
|
|||||||
|
|
||||||
#endif // FMT_USE_CONSTEXPR
|
#endif // FMT_USE_CONSTEXPR
|
||||||
|
|
||||||
// C++20 feature test, since r346892 Clang considers char8_t a fundamental
|
|
||||||
// type in this mode. If this is the case __cpp_char8_t will be defined.
|
|
||||||
#ifndef __cpp_char8_t
|
|
||||||
// Locally provide type char8_t defined in format.h
|
|
||||||
using fmt::char8_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Convert 'char8_t' character sequences to 'char' sequences
|
|
||||||
// Otherwise GTest will insist on inserting 'char8_t' NTBS into a 'char' stream,
|
|
||||||
// but basic_ostream<char>::operator<< overloads taking 'char8_t' arguments
|
|
||||||
// are defined as deleted by P1423.
|
|
||||||
// Handling individual 'char8_t's is done inline.
|
|
||||||
std::string from_u8str(const std::basic_string<char8_t>& str) {
|
|
||||||
return std::string(str.begin(), str.end());
|
|
||||||
}
|
|
||||||
std::string from_u8str(const fmt::basic_string_view<char8_t>& str) {
|
|
||||||
return std::string(str.begin(), str.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(FormatTest, EmphasisNonHeaderOnly) {
|
TEST(FormatTest, EmphasisNonHeaderOnly) {
|
||||||
// Ensure this compiles even if FMT_HEADER_ONLY is not defined.
|
// Ensure this compiles even if FMT_HEADER_ONLY is not defined.
|
||||||
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"),
|
EXPECT_EQ(fmt::format(fmt::emphasis::bold, "bold error"),
|
||||||
@ -2561,10 +2598,18 @@ TEST(FormatTest, FormatCustomChar) {
|
|||||||
EXPECT_EQ(result[0], mychar('x'));
|
EXPECT_EQ(result[0], mychar('x'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
template <typename S> std::string from_u8str(const S& str) {
|
||||||
|
return std::string(str.begin(), str.end());
|
||||||
|
}
|
||||||
|
|
||||||
TEST(FormatTest, FormatUTF8Precision) {
|
TEST(FormatTest, FormatUTF8Precision) {
|
||||||
using str_type = std::basic_string<char8_t>;
|
using str_type = std::basic_string<fmt::internal::char8_type>;
|
||||||
str_type format(reinterpret_cast<const char8_t*>(u8"{:.4}"));
|
str_type format(
|
||||||
str_type str(reinterpret_cast<const char8_t*>(u8"caf\u00e9s")); // cafés
|
reinterpret_cast<const fmt::internal::char8_type*>(u8"{:.4}"));
|
||||||
|
str_type str(reinterpret_cast<const fmt::internal::char8_type*>(
|
||||||
|
u8"caf\u00e9s")); // cafés
|
||||||
auto result = fmt::format(format, str);
|
auto result = fmt::format(format, str);
|
||||||
EXPECT_EQ(fmt::internal::count_code_points(result), 4);
|
EXPECT_EQ(fmt::internal::count_code_points(result), 4);
|
||||||
EXPECT_EQ(result.size(), 5);
|
EXPECT_EQ(result.size(), 5);
|
||||||
|
|||||||
@ -7,14 +7,19 @@ in fmt. It is a part of the continous fuzzing at
|
|||||||
The source code is modified to make the fuzzing possible without locking up on
|
The source code is modified to make the fuzzing possible without locking up on
|
||||||
resource exhaustion:
|
resource exhaustion:
|
||||||
```cpp
|
```cpp
|
||||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
#ifdef FMT_FUZZ
|
||||||
if(spec.precision>100000) {
|
if(spec.precision>100000) {
|
||||||
throw std::runtime_error("fuzz mode - avoiding large precision");
|
throw std::runtime_error("fuzz mode - avoiding large precision");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
```
|
```
|
||||||
This macro is the defacto standard for making fuzzing practically possible, see
|
This macro `FMT_FUZZ` is enabled on OSS-Fuzz builds and makes fuzzing
|
||||||
[the libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode).
|
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
|
||||||
|
|
||||||
|
|||||||
@ -29,11 +29,11 @@ void invoke_fmt(const uint8_t* Data, std::size_t Size, unsigned int argsize) {
|
|||||||
// allocating buffers separately is slower, but increases chances
|
// allocating buffers separately is slower, but increases chances
|
||||||
// of detecting memory errors
|
// of detecting memory errors
|
||||||
#if FMT_FUZZ_SEPARATE_ALLOCATION
|
#if FMT_FUZZ_SEPARATE_ALLOCATION
|
||||||
std::vector<char> argnamebuffer(argsize);
|
std::vector<char> argnamebuffer(argsize + 1);
|
||||||
std::memcpy(argnamebuffer.data(), Data, argsize);
|
std::memcpy(argnamebuffer.data(), Data, argsize);
|
||||||
auto argname = fmt::string_view(argnamebuffer.data(), argsize);
|
auto argname = argnamebuffer.data();
|
||||||
#else
|
#else
|
||||||
auto argname = fmt::string_view(fmt_fuzzer::as_chars(Data), argsize);
|
auto argname = fmt_fuzzer::as_chars(Data);
|
||||||
#endif
|
#endif
|
||||||
Data += argsize;
|
Data += argsize;
|
||||||
Size -= argsize;
|
Size -= argsize;
|
||||||
|
|||||||
@ -489,9 +489,9 @@ TEST(FileTest, Fdopen) {
|
|||||||
|
|
||||||
# ifdef FMT_LOCALE
|
# ifdef FMT_LOCALE
|
||||||
TEST(LocaleTest, Strtod) {
|
TEST(LocaleTest, Strtod) {
|
||||||
fmt::Locale locale;
|
fmt::locale loc;
|
||||||
const char *start = "4.2", *ptr = start;
|
const char *start = "4.2", *ptr = start;
|
||||||
EXPECT_EQ(4.2, locale.strtod(ptr));
|
EXPECT_EQ(4.2, loc.strtod(ptr));
|
||||||
EXPECT_EQ(start + 3, ptr);
|
EXPECT_EQ(start + 3, ptr);
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|||||||
@ -11,13 +11,15 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "posix-mock.h"
|
#include "posix-mock.h"
|
||||||
#include "../src/os.cc"
|
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include "../src/os.cc"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
# undef max
|
# undef max
|
||||||
@ -51,7 +53,7 @@ std::size_t read_nbyte;
|
|||||||
std::size_t write_nbyte;
|
std::size_t write_nbyte;
|
||||||
bool sysconf_error;
|
bool sysconf_error;
|
||||||
|
|
||||||
enum FStatSimulation { NONE, MAX_SIZE, ERROR } fstat_sim;
|
enum { NONE, MAX_SIZE, ERROR } fstat_sim;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#define EMULATE_EINTR(func, error_result) \
|
#define EMULATE_EINTR(func, error_result) \
|
||||||
@ -215,10 +217,10 @@ TEST(UtilTest, GetPageSize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, OpenRetry) {
|
TEST(FileTest, OpenRetry) {
|
||||||
write_file("test", "there must be something here");
|
write_file("temp", "there must be something here");
|
||||||
std::unique_ptr<file> f{nullptr};
|
std::unique_ptr<file> f{nullptr};
|
||||||
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)), open,
|
EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
|
||||||
"cannot open file test");
|
"cannot open file temp");
|
||||||
# ifndef _WIN32
|
# ifndef _WIN32
|
||||||
char c = 0;
|
char c = 0;
|
||||||
f->read(&c, 1);
|
f->read(&c, 1);
|
||||||
@ -230,14 +232,15 @@ TEST(FileTest, CloseNoRetryInDtor) {
|
|||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
std::unique_ptr<file> f(new file(std::move(read_end)));
|
std::unique_ptr<file> f(new file(std::move(read_end)));
|
||||||
int saved_close_count = 0;
|
int saved_close_count = 0;
|
||||||
EXPECT_WRITE(stderr,
|
EXPECT_WRITE(
|
||||||
{
|
stderr,
|
||||||
close_count = 1;
|
{
|
||||||
f.reset(nullptr);
|
close_count = 1;
|
||||||
saved_close_count = close_count;
|
f.reset(nullptr);
|
||||||
close_count = 0;
|
saved_close_count = close_count;
|
||||||
},
|
close_count = 0;
|
||||||
format_system_error(EINTR, "cannot close file") + "\n");
|
},
|
||||||
|
format_system_error(EINTR, "cannot close file") + "\n");
|
||||||
EXPECT_EQ(2, saved_close_count);
|
EXPECT_EQ(2, saved_close_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,8 +255,8 @@ TEST(FileTest, CloseNoRetry) {
|
|||||||
|
|
||||||
TEST(FileTest, Size) {
|
TEST(FileTest, Size) {
|
||||||
std::string content = "top secret, destroy before reading";
|
std::string content = "top secret, destroy before reading";
|
||||||
write_file("test", content);
|
write_file("temp", content);
|
||||||
file f("test", file::RDONLY);
|
file f("temp", file::RDONLY);
|
||||||
EXPECT_GE(f.size(), 0);
|
EXPECT_GE(f.size(), 0);
|
||||||
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
|
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
|
||||||
# ifdef _WIN32
|
# ifdef _WIN32
|
||||||
@ -270,8 +273,8 @@ TEST(FileTest, Size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FileTest, MaxSize) {
|
TEST(FileTest, MaxSize) {
|
||||||
write_file("test", "");
|
write_file("temp", "");
|
||||||
file f("test", file::RDONLY);
|
file f("temp", file::RDONLY);
|
||||||
fstat_sim = MAX_SIZE;
|
fstat_sim = MAX_SIZE;
|
||||||
EXPECT_GE(f.size(), 0);
|
EXPECT_GE(f.size(), 0);
|
||||||
EXPECT_EQ(max_file_size(), f.size());
|
EXPECT_EQ(max_file_size(), f.size());
|
||||||
@ -385,10 +388,10 @@ TEST(FileTest, FdopenNoRetry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(BufferedFileTest, OpenRetry) {
|
TEST(BufferedFileTest, OpenRetry) {
|
||||||
write_file("test", "there must be something here");
|
write_file("temp", "there must be something here");
|
||||||
std::unique_ptr<buffered_file> f{nullptr};
|
std::unique_ptr<buffered_file> f{nullptr};
|
||||||
EXPECT_RETRY(f.reset(new buffered_file("test", "r")), fopen,
|
EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen,
|
||||||
"cannot open file test");
|
"cannot open file temp");
|
||||||
# ifndef _WIN32
|
# ifndef _WIN32
|
||||||
char c = 0;
|
char c = 0;
|
||||||
if (fread(&c, 1, 1, f->get()) < 1)
|
if (fread(&c, 1, 1, f->get()) < 1)
|
||||||
@ -401,14 +404,15 @@ TEST(BufferedFileTest, CloseNoRetryInDtor) {
|
|||||||
file::pipe(read_end, write_end);
|
file::pipe(read_end, write_end);
|
||||||
std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
|
std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
|
||||||
int saved_fclose_count = 0;
|
int saved_fclose_count = 0;
|
||||||
EXPECT_WRITE(stderr,
|
EXPECT_WRITE(
|
||||||
{
|
stderr,
|
||||||
fclose_count = 1;
|
{
|
||||||
f.reset(nullptr);
|
fclose_count = 1;
|
||||||
saved_fclose_count = fclose_count;
|
f.reset(nullptr);
|
||||||
fclose_count = 0;
|
saved_fclose_count = fclose_count;
|
||||||
},
|
fclose_count = 0;
|
||||||
format_system_error(EINTR, "cannot close file") + "\n");
|
},
|
||||||
|
format_system_error(EINTR, "cannot close file") + "\n");
|
||||||
EXPECT_EQ(2, saved_fclose_count);
|
EXPECT_EQ(2, saved_fclose_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,33 +437,33 @@ TEST(BufferedFileTest, FilenoNoRetry) {
|
|||||||
}
|
}
|
||||||
#endif // FMT_USE_FCNTL
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
||||||
struct TestMock {
|
struct test_mock {
|
||||||
static TestMock* instance;
|
static test_mock* instance;
|
||||||
} * TestMock::instance;
|
} * test_mock::instance;
|
||||||
|
|
||||||
TEST(ScopedMock, Scope) {
|
TEST(ScopedMock, Scope) {
|
||||||
{
|
{
|
||||||
ScopedMock<TestMock> mock;
|
ScopedMock<test_mock> mock;
|
||||||
EXPECT_EQ(&mock, TestMock::instance);
|
EXPECT_EQ(&mock, test_mock::instance);
|
||||||
TestMock& copy = mock;
|
test_mock& copy = mock;
|
||||||
static_cast<void>(copy);
|
static_cast<void>(copy);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(nullptr, TestMock::instance);
|
EXPECT_EQ(nullptr, test_mock::instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FMT_LOCALE
|
#ifdef FMT_LOCALE
|
||||||
|
|
||||||
typedef fmt::Locale::type LocaleType;
|
typedef fmt::locale::type locale_type;
|
||||||
|
|
||||||
struct LocaleMock {
|
struct locale_mock {
|
||||||
static LocaleMock* instance;
|
static locale_mock* instance;
|
||||||
MOCK_METHOD3(newlocale, LocaleType(int category_mask, const char* locale,
|
MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale,
|
||||||
LocaleType base));
|
locale_type base));
|
||||||
MOCK_METHOD1(freelocale, void(LocaleType locale));
|
MOCK_METHOD1(freelocale, void(locale_type locale));
|
||||||
|
|
||||||
MOCK_METHOD3(strtod_l,
|
MOCK_METHOD3(strtod_l,
|
||||||
double(const char* nptr, char** endptr, LocaleType locale));
|
double(const char* nptr, char** endptr, locale_type locale));
|
||||||
} * LocaleMock::instance;
|
} * locale_mock::instance;
|
||||||
|
|
||||||
# ifdef _MSC_VER
|
# ifdef _MSC_VER
|
||||||
# pragma warning(push)
|
# pragma warning(push)
|
||||||
@ -470,15 +474,15 @@ struct LocaleMock {
|
|||||||
# endif
|
# endif
|
||||||
|
|
||||||
_locale_t _create_locale(int category, const char* locale) {
|
_locale_t _create_locale(int category, const char* locale) {
|
||||||
return LocaleMock::instance->newlocale(category, locale, 0);
|
return locale_mock::instance->newlocale(category, locale, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _free_locale(_locale_t locale) {
|
void _free_locale(_locale_t locale) {
|
||||||
LocaleMock::instance->freelocale(locale);
|
locale_mock::instance->freelocale(locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
|
double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
|
||||||
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
|
return locale_mock::instance->strtod_l(nptr, endptr, locale);
|
||||||
}
|
}
|
||||||
# ifdef __clang__
|
# ifdef __clang__
|
||||||
# pragma clang diagnostic pop
|
# pragma clang diagnostic pop
|
||||||
@ -492,11 +496,6 @@ double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
|
|||||||
# define FMT_LOCALE_THROW
|
# define FMT_LOCALE_THROW
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
LocaleType newlocale(int category_mask, const char* locale,
|
|
||||||
LocaleType base) FMT_LOCALE_THROW {
|
|
||||||
return LocaleMock::instance->newlocale(category_mask, locale, base);
|
|
||||||
}
|
|
||||||
|
|
||||||
# if defined(__APPLE__) || \
|
# if defined(__APPLE__) || \
|
||||||
(defined(__FreeBSD__) && __FreeBSD_version < 1200002)
|
(defined(__FreeBSD__) && __FreeBSD_version < 1200002)
|
||||||
typedef int FreeLocaleResult;
|
typedef int FreeLocaleResult;
|
||||||
@ -504,49 +503,55 @@ typedef int FreeLocaleResult;
|
|||||||
typedef void FreeLocaleResult;
|
typedef void FreeLocaleResult;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW {
|
FreeLocaleResult freelocale(locale_type locale) FMT_LOCALE_THROW {
|
||||||
LocaleMock::instance->freelocale(locale);
|
locale_mock::instance->freelocale(locale);
|
||||||
return FreeLocaleResult();
|
return FreeLocaleResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
double strtod_l(const char* nptr, char** endptr,
|
double strtod_l(const char* nptr, char** endptr,
|
||||||
LocaleType locale) FMT_LOCALE_THROW {
|
locale_type locale) FMT_LOCALE_THROW {
|
||||||
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
|
return locale_mock::instance->strtod_l(nptr, endptr, locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
# undef FMT_LOCALE_THROW
|
# undef FMT_LOCALE_THROW
|
||||||
|
|
||||||
TEST(LocaleTest, LocaleMock) {
|
# ifndef _WIN32
|
||||||
ScopedMock<LocaleMock> mock;
|
locale_t test::newlocale(int category_mask, const char* locale, locale_t base) {
|
||||||
LocaleType locale = reinterpret_cast<LocaleType>(11);
|
return locale_mock::instance->newlocale(category_mask, locale, base);
|
||||||
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
|
|
||||||
newlocale(222, "foo", locale);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(LocaleTest, LocaleMock) {
|
||||||
|
ScopedMock<locale_mock> mock;
|
||||||
|
locale_type locale = reinterpret_cast<locale_type>(11);
|
||||||
|
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
|
||||||
|
FMT_SYSTEM(newlocale(222, "foo", locale));
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
TEST(LocaleTest, Locale) {
|
TEST(LocaleTest, Locale) {
|
||||||
# ifndef LC_NUMERIC_MASK
|
# ifndef LC_NUMERIC_MASK
|
||||||
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
||||||
# endif
|
# endif
|
||||||
ScopedMock<LocaleMock> mock;
|
ScopedMock<locale_mock> mock;
|
||||||
LocaleType impl = reinterpret_cast<LocaleType>(42);
|
locale_type impl = reinterpret_cast<locale_type>(42);
|
||||||
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
|
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
|
||||||
.WillOnce(Return(impl));
|
.WillOnce(Return(impl));
|
||||||
EXPECT_CALL(mock, freelocale(impl));
|
EXPECT_CALL(mock, freelocale(impl));
|
||||||
fmt::Locale locale;
|
fmt::locale loc;
|
||||||
EXPECT_EQ(impl, locale.get());
|
EXPECT_EQ(impl, loc.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LocaleTest, Strtod) {
|
TEST(LocaleTest, Strtod) {
|
||||||
ScopedMock<LocaleMock> mock;
|
ScopedMock<locale_mock> mock;
|
||||||
EXPECT_CALL(mock, newlocale(_, _, _))
|
EXPECT_CALL(mock, newlocale(_, _, _))
|
||||||
.WillOnce(Return(reinterpret_cast<LocaleType>(42)));
|
.WillOnce(Return(reinterpret_cast<locale_type>(42)));
|
||||||
EXPECT_CALL(mock, freelocale(_));
|
EXPECT_CALL(mock, freelocale(_));
|
||||||
fmt::Locale locale;
|
fmt::locale loc;
|
||||||
const char* str = "4.2";
|
const char* str = "4.2";
|
||||||
char end = 'x';
|
char end = 'x';
|
||||||
EXPECT_CALL(mock, strtod_l(str, _, locale.get()))
|
EXPECT_CALL(mock, strtod_l(str, _, loc.get()))
|
||||||
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));
|
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));
|
||||||
EXPECT_EQ(777, locale.strtod(str));
|
EXPECT_EQ(777, loc.strtod(str));
|
||||||
EXPECT_EQ(&end, str);
|
EXPECT_EQ(&end, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,11 @@
|
|||||||
#define FMT_POSIX_TEST_H
|
#define FMT_POSIX_TEST_H
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <locale.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#ifdef __APPLE__
|
||||||
|
# include <xlocale.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
@ -62,6 +66,10 @@ int pipe(int* pfds, unsigned psize, int textmode);
|
|||||||
FILE* fopen(const char* filename, const char* mode);
|
FILE* fopen(const char* filename, const char* mode);
|
||||||
int fclose(FILE* stream);
|
int fclose(FILE* stream);
|
||||||
int(fileno)(FILE* stream);
|
int(fileno)(FILE* stream);
|
||||||
|
|
||||||
|
#if defined(FMT_LOCALE) && !defined(_WIN32)
|
||||||
|
locale_t newlocale(int category_mask, const char* locale, locale_t base);
|
||||||
|
#endif
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|
||||||
#define FMT_SYSTEM(call) test::call
|
#define FMT_SYSTEM(call) test::call
|
||||||
|
|||||||
@ -113,14 +113,14 @@ TEST(PrintfTest, SwitchArgIndexing) {
|
|||||||
|
|
||||||
TEST(PrintfTest, InvalidArgIndex) {
|
TEST(PrintfTest, InvalidArgIndex) {
|
||||||
EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error,
|
||||||
"argument index out of range");
|
"argument not found");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
|
||||||
"argument index out of range");
|
"argument not found");
|
||||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), format_error,
|
||||||
"argument index out of range");
|
"argument not found");
|
||||||
|
|
||||||
EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error,
|
||||||
"argument index out of range");
|
"argument not found");
|
||||||
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
}
|
}
|
||||||
@ -223,7 +223,7 @@ TEST(PrintfTest, DynamicWidth) {
|
|||||||
EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error,
|
||||||
"width is not integer");
|
"width is not integer");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%*d"), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%*d"), format_error,
|
||||||
"argument index out of range");
|
"argument not found");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
}
|
}
|
||||||
@ -259,6 +259,11 @@ TEST(PrintfTest, FloatPrecision) {
|
|||||||
EXPECT_PRINTF(buffer, "%.3a", 1234.5678);
|
EXPECT_PRINTF(buffer, "%.3a", 1234.5678);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(PrintfTest, StringPrecision) {
|
||||||
|
char test[] = {'H', 'e', 'l', 'l', 'o'};
|
||||||
|
EXPECT_EQ(fmt::sprintf("%.4s", test), "Hell");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, IgnorePrecisionForNonNumericArg) {
|
TEST(PrintfTest, IgnorePrecisionForNonNumericArg) {
|
||||||
EXPECT_PRINTF("abc", "%.5s", "abc");
|
EXPECT_PRINTF("abc", "%.5s", "abc");
|
||||||
}
|
}
|
||||||
@ -269,7 +274,7 @@ TEST(PrintfTest, DynamicPrecision) {
|
|||||||
EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
|
||||||
"precision is not integer");
|
"precision is not integer");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error,
|
||||||
"argument index out of range");
|
"argument not found");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
if (sizeof(long long) != sizeof(int)) {
|
if (sizeof(long long) != sizeof(int)) {
|
||||||
@ -447,6 +452,12 @@ TEST(PrintfTest, String) {
|
|||||||
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
|
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(PrintfTest, UCharString) {
|
||||||
|
unsigned char str[] = "test";
|
||||||
|
unsigned char* pstr = str;
|
||||||
|
EXPECT_EQ("test", fmt::sprintf("%s", pstr));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Pointer) {
|
TEST(PrintfTest, Pointer) {
|
||||||
int n;
|
int n;
|
||||||
void* p = &n;
|
void* p = &n;
|
||||||
@ -502,9 +513,7 @@ TEST(PrintfTest, PrintfError) {
|
|||||||
TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); }
|
TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); }
|
||||||
|
|
||||||
TEST(PrintfTest, PrintfCustom) {
|
TEST(PrintfTest, PrintfCustom) {
|
||||||
// The test is disabled for now because it requires decoupling
|
EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
|
||||||
// fallback_formatter::format from format_context.
|
|
||||||
//EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, OStream) {
|
TEST(PrintfTest, OStream) {
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
// {fmt} support for ranges, containers and types tuple interface.
|
// {fmt} support for ranges, containers and types tuple interface.
|
||||||
|
|
||||||
#include "fmt/ranges.h"
|
#include "fmt/ranges.h"
|
||||||
|
|
||||||
#include "gtest.h"
|
#include "gtest.h"
|
||||||
|
|
||||||
// Check if 'if constexpr' is supported.
|
// Check if 'if constexpr' is supported.
|
||||||
@ -44,9 +45,10 @@ TEST(RangesTest, FormatPair) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(RangesTest, FormatTuple) {
|
TEST(RangesTest, FormatTuple) {
|
||||||
std::tuple<int64_t, float, std::string, char> tu1{42, 1.5f, "this is tuple",
|
std::tuple<int64_t, float, std::string, char> t{42, 1.5f, "this is tuple",
|
||||||
'i'};
|
'i'};
|
||||||
EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format("{}", tu1));
|
EXPECT_EQ("(42, 1.5, \"this is tuple\", 'i')", fmt::format("{}", t));
|
||||||
|
EXPECT_EQ("()", fmt::format("{}", std::tuple<>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RangesTest, JoinTuple) {
|
TEST(RangesTest, JoinTuple) {
|
||||||
@ -68,6 +70,12 @@ TEST(RangesTest, JoinTuple) {
|
|||||||
EXPECT_EQ("4.0", fmt::format("{}", fmt::join(t4, "/")));
|
EXPECT_EQ("4.0", fmt::format("{}", fmt::join(t4, "/")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(RangesTest, JoinInitializerList) {
|
||||||
|
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join({1, 2, 3}, ", ")));
|
||||||
|
EXPECT_EQ("fmt rocks !",
|
||||||
|
fmt::format("{}", fmt::join({"fmt", "rocks", "!"}, " ")));
|
||||||
|
}
|
||||||
|
|
||||||
struct my_struct {
|
struct my_struct {
|
||||||
int32_t i;
|
int32_t i;
|
||||||
std::string str; // can throw
|
std::string str; // can throw
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user