1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 00:37:15 +01:00

Update formatting library.

This commit is contained in:
Sandu Liviu Catalin 2022-03-16 22:36:44 +02:00
parent 38f0a53cd8
commit e253dc2038
66 changed files with 6165 additions and 4591 deletions

View File

@ -81,6 +81,7 @@ 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) option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
option(FMT_MODULE "Build a module instead of a traditional library." OFF) option(FMT_MODULE "Build a module instead of a traditional library." OFF)
option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF)
set(FMT_CAN_MODULE OFF) set(FMT_CAN_MODULE OFF)
if (CMAKE_CXX_STANDARD GREATER 17 AND if (CMAKE_CXX_STANDARD GREATER 17 AND
@ -96,6 +97,10 @@ if (FMT_TEST AND FMT_MODULE)
# The tests require {fmt} to be compiled as traditional library # The tests require {fmt} to be compiled as traditional library
message(STATUS "Testing is incompatible with build mode 'module'.") message(STATUS "Testing is incompatible with build mode 'module'.")
endif () endif ()
set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
if (FMT_SYSTEM_HEADERS)
set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
endif ()
# Get version from core.h # Get version from core.h
file(READ include/fmt/core.h core_h) file(READ include/fmt/core.h core_h)
@ -151,7 +156,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-Wcast-align -Wcast-align
-Wctor-dtor-privacy -Wdisabled-optimization -Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Woverloaded-virtual -Winvalid-pch -Woverloaded-virtual
-Wconversion -Wswitch-enum -Wundef -Wconversion -Wundef
-Wno-ctor-dtor-privacy -Wno-format-nonliteral) -Wno-ctor-dtor-privacy -Wno-format-nonliteral)
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
@ -204,18 +209,6 @@ if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*") ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
endif () endif ()
set(strtod_l_headers stdlib.h)
if (APPLE)
set(strtod_l_headers ${strtod_l_headers} xlocale.h)
endif ()
include(CheckSymbolExists)
if (WIN32)
check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
else ()
check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
endif ()
function(add_headers VAR) function(add_headers VAR)
set(headers ${${VAR}}) set(headers ${${VAR}})
foreach (header ${ARGN}) foreach (header ${ARGN})
@ -239,17 +232,6 @@ 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)
if (HAVE_STRTOD_L)
target_compile_definitions(fmt PUBLIC FMT_LOCALE)
endif ()
if (MINGW)
check_cxx_compiler_flag("Wa,-mbig-obj" FMT_HAS_MBIG_OBJ)
if (${FMT_HAS_MBIG_OBJ})
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
endif()
endif ()
if (FMT_WERROR) if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG}) target_compile_options(fmt PRIVATE ${WERROR_FLAG})
endif () endif ()
@ -262,7 +244,7 @@ endif ()
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES}) target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt PUBLIC target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>) $<INSTALL_INTERFACE:${FMT_INC_DIR}>)
@ -270,6 +252,7 @@ set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
set_target_properties(fmt PROPERTIES set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR} VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
PUBLIC_HEADER "${FMT_HEADERS}"
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}") DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target # Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
@ -298,7 +281,7 @@ add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1) target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES}) target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt-header-only INTERFACE target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>) $<INSTALL_INTERFACE:${FMT_INC_DIR}>)
@ -347,6 +330,8 @@ if (FMT_INSTALL)
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
LIBRARY DESTINATION ${FMT_LIB_DIR} LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR} ARCHIVE DESTINATION ${FMT_LIB_DIR}
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
FRAMEWORK DESTINATION "."
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
# Use a namespace because CMake provides better diagnostics for namespaced # Use a namespace because CMake provides better diagnostics for namespaced
@ -363,7 +348,6 @@ if (FMT_INSTALL)
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}> install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL) DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif () endif ()

View File

@ -1,3 +1,374 @@
8.1.1 - 2022-01-06
------------------
* Restored ABI compatibility with version 8.0.x
(`#2695 <https://github.com/fmtlib/fmt/issues/2695>`_,
`#2696 <https://github.com/fmtlib/fmt/pull/2696>`_).
Thanks `@saraedum (Julian Rüth) <https://github.com/saraedum>`_.
* Fixed chrono formatting on big endian systems
(`#2698 <https://github.com/fmtlib/fmt/issues/2698>`_,
`#2699 <https://github.com/fmtlib/fmt/pull/2699>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and
`@xvitaly (Vitaly Zaitsev) <https://github.com/xvitaly>`_.
* Fixed a linkage error with mingw
(`#2691 <https://github.com/fmtlib/fmt/issues/2691>`_,
`#2692 <https://github.com/fmtlib/fmt/pull/2692>`_).
Thanks `@rbberger (Richard Berger) <https://github.com/rbberger>`_.
8.1.0 - 2022-01-02
------------------
* Optimized chrono formatting
(`#2500 <https://github.com/fmtlib/fmt/pull/2500>`_,
`#2537 <https://github.com/fmtlib/fmt/pull/2537>`_,
`#2541 <https://github.com/fmtlib/fmt/issues/2541>`_,
`#2544 <https://github.com/fmtlib/fmt/pull/2544>`_,
`#2550 <https://github.com/fmtlib/fmt/pull/2550>`_,
`#2551 <https://github.com/fmtlib/fmt/pull/2551>`_,
`#2576 <https://github.com/fmtlib/fmt/pull/2576>`_,
`#2577 <https://github.com/fmtlib/fmt/issues/2577>`_,
`#2586 <https://github.com/fmtlib/fmt/pull/2586>`_,
`#2591 <https://github.com/fmtlib/fmt/pull/2591>`_,
`#2594 <https://github.com/fmtlib/fmt/pull/2594>`_,
`#2602 <https://github.com/fmtlib/fmt/pull/2602>`_,
`#2617 <https://github.com/fmtlib/fmt/pull/2617>`_,
`#2628 <https://github.com/fmtlib/fmt/issues/2628>`_,
`#2633 <https://github.com/fmtlib/fmt/pull/2633>`_,
`#2670 <https://github.com/fmtlib/fmt/issues/2670>`_,
`#2671 <https://github.com/fmtlib/fmt/pull/2671>`_).
Processing of some specifiers such as ``%z`` and ``%Y`` is now up to 10-20
times faster, for example on GCC 11 with libstdc++::
----------------------------------------------------------------------------
Benchmark Before After
----------------------------------------------------------------------------
FMTFormatter_z 261 ns 26.3 ns
FMTFormatterCompile_z 246 ns 11.6 ns
FMTFormatter_Y 263 ns 26.1 ns
FMTFormatterCompile_Y 244 ns 10.5 ns
----------------------------------------------------------------------------
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and
`@toughengineer (Pavel Novikov) <https://github.com/toughengineer>`_.
* Implemented subsecond formatting for chrono durations
(`#2623 <https://github.com/fmtlib/fmt/pull/2623>`_).
For example (`godbolt <https://godbolt.org/z/es7vWTETe>`__):
.. code:: c++
#include <fmt/chrono.h>
int main() {
fmt::print("{:%S}", std::chrono::milliseconds(1234));
}
prints "01.234".
Thanks `@matrackif <https://github.com/matrackif>`_.
* Fixed handling of precision 0 when formatting chrono durations
(`#2587 <https://github.com/fmtlib/fmt/issues/2587>`_,
`#2588 <https://github.com/fmtlib/fmt/pull/2588>`_).
Thanks `@lukester1975 <https://github.com/lukester1975>`_.
* Fixed an overflow on invalid inputs in the ``tm`` formatter
(`#2564 <https://github.com/fmtlib/fmt/pull/2564>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Added ``fmt::group_digits`` that formats integers with a non-localized digit
separator (comma) for groups of three digits.
For example (`godbolt <https://godbolt.org/z/TxGxG9Poq>`__):
.. code:: c++
#include <fmt/format.h>
int main() {
fmt::print("{} dollars", fmt::group_digits(1000000));
}
prints "1,000,000 dollars".
* Added support for faint, conceal, reverse and blink text styles
(`#2394 <https://github.com/fmtlib/fmt/pull/2394>`_):
https://user-images.githubusercontent.com/576385/147710227-c68f5317-f8fa-42c3-9123-7c4ba3c398cb.mp4
Thanks `@benit8 (Benoît Lormeau) <https://github.com/benit8>`_ and
`@data-man (Dmitry Atamanov) <https://github.com/data-man>`_.
* Added experimental support for compile-time floating point formatting
(`#2426 <https://github.com/fmtlib/fmt/pull/2426>`_,
`#2470 <https://github.com/fmtlib/fmt/pull/2470>`_).
It is currently limited to the header-only mode.
Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_.
* Added UDL-based named argument support to compile-time format string checks
(`#2640 <https://github.com/fmtlib/fmt/issues/2640>`_,
`#2649 <https://github.com/fmtlib/fmt/pull/2649>`_).
For example (`godbolt <https://godbolt.org/z/ohGbbvonv>`__):
.. code:: c++
#include <fmt/format.h>
int main() {
using namespace fmt::literals;
fmt::print("{answer:s}", "answer"_a=42);
}
gives a compile-time error on compilers with C++20 ``consteval`` and non-type
template parameter support (gcc 10+) because ``s`` is not a valid format
specifier for an integer.
Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_.
* Implemented escaping of string range elements.
For example (`godbolt <https://godbolt.org/z/rKvM1vKf3>`__):
.. code:: c++
#include <fmt/ranges.h>
#include <vector>
int main() {
fmt::print("{}", std::vector<std::string>{"\naan"});
}
is now printed as::
["\naan"]
instead of::
["
aan"]
* Switched to JSON-like representation of maps and sets for consistency with
Python's ``str.format``.
For example (`godbolt <https://godbolt.org/z/seKjoY9W5>`__):
.. code:: c++
#include <fmt/ranges.h>
#include <map>
int main() {
fmt::print("{}", std::map<std::string, int>{{"answer", 42}});
}
is now printed as::
{"answer": 42}
* Extended ``fmt::join`` to support C++20-only ranges
(`#2549 <https://github.com/fmtlib/fmt/pull/2549>`_).
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Optimized handling of non-const-iterable ranges and implemented initial
support for non-const-formattable types.
* Disabled implicit conversions of scoped enums to integers that was
accidentally introduced in earlier versions
(`#1841 <https://github.com/fmtlib/fmt/pull/1841>`_).
* Deprecated implicit conversion of ``[const] signed char*`` and
``[const] unsigned char*`` to C strings.
* Deprecated ``_format``, a legacy UDL-based format API
(`#2646 <https://github.com/fmtlib/fmt/pull/2646>`_).
Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_.
* Marked ``format``, ``formatted_size`` and ``to_string`` as ``[[nodiscard]]``
(`#2612 <https://github.com/fmtlib/fmt/pull/2612>`_).
`@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_.
* Added missing diagnostic when trying to format function and member pointers
as well as objects convertible to pointers which is explicitly disallowed
(`#2598 <https://github.com/fmtlib/fmt/issues/2598>`_,
`#2609 <https://github.com/fmtlib/fmt/pull/2609>`_,
`#2610 <https://github.com/fmtlib/fmt/pull/2610>`_).
Thanks `@AlexGuteniev (Alex Guteniev) <https://github.com/AlexGuteniev>`_.
* Optimized writing to a contiguous buffer with ``format_to_n``
(`#2489 <https://github.com/fmtlib/fmt/pull/2489>`_).
Thanks `@Roman-Koshelev <https://github.com/Roman-Koshelev>`_.
* Optimized writing to non-``char`` buffers
(`#2477 <https://github.com/fmtlib/fmt/pull/2477>`_).
Thanks `@Roman-Koshelev <https://github.com/Roman-Koshelev>`_.
* Decimal point is now localized when using the ``L`` specifier.
* Improved floating point formatter implementation
(`#2498 <https://github.com/fmtlib/fmt/pull/2498>`_,
`#2499 <https://github.com/fmtlib/fmt/pull/2499>`_).
Thanks `@Roman-Koshelev <https://github.com/Roman-Koshelev>`_.
* Fixed handling of very large precision in fixed format
(`#2616 <https://github.com/fmtlib/fmt/pull/2616>`_).
* Made a table of cached powers used in FP formatting static
(`#2509 <https://github.com/fmtlib/fmt/pull/2509>`_).
Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_.
* Resolved a lookup ambiguity with C++20 format-related functions due to ADL
(`#2639 <https://github.com/fmtlib/fmt/issues/2639>`_,
`#2641 <https://github.com/fmtlib/fmt/pull/2641>`_).
Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_.
* Removed unnecessary inline namespace qualification
(`#2642 <https://github.com/fmtlib/fmt/issues/2642>`_,
`#2643 <https://github.com/fmtlib/fmt/pull/2643>`_).
Thanks `@mkurdej (Marek Kurdej) <https://github.com/mkurdej>`_.
* Implemented argument forwarding in ``format_to_n``
(`#2462 <https://github.com/fmtlib/fmt/issues/2462>`_,
`#2463 <https://github.com/fmtlib/fmt/pull/2463>`_).
Thanks `@owent (WenTao Ou) <https://github.com/owent>`_.
* Fixed handling of implicit conversions in ``fmt::to_string`` and format string
compilation (`#2565 <https://github.com/fmtlib/fmt/issues/2565>`_).
* Changed the default access mode of files created by ``fmt::output_file`` to
``-rw-r--r--`` for consistency with ``fopen``
(`#2530 <https://github.com/fmtlib/fmt/issues/2530>`_).
* Make ``fmt::ostream::flush`` public
(`#2435 <https://github.com/fmtlib/fmt/issues/2435>`_).
* Improved C++14/17 attribute detection
(`#2615 <https://github.com/fmtlib/fmt/pull/2615>`_).
Thanks `@AlexGuteniev (Alex Guteniev) <https://github.com/AlexGuteniev>`_.
* Improved ``consteval`` detection for MSVC
(`#2559 <https://github.com/fmtlib/fmt/pull/2559>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Improved documentation
(`#2406 <https://github.com/fmtlib/fmt/issues/2406>`_,
`#2446 <https://github.com/fmtlib/fmt/pull/2446>`_,
`#2493 <https://github.com/fmtlib/fmt/issues/2493>`_,
`#2513 <https://github.com/fmtlib/fmt/issues/2513>`_,
`#2515 <https://github.com/fmtlib/fmt/pull/2515>`_,
`#2522 <https://github.com/fmtlib/fmt/issues/2522>`_,
`#2562 <https://github.com/fmtlib/fmt/pull/2562>`_,
`#2575 <https://github.com/fmtlib/fmt/pull/2575>`_,
`#2606 <https://github.com/fmtlib/fmt/pull/2606>`_,
`#2620 <https://github.com/fmtlib/fmt/pull/2620>`_,
`#2676 <https://github.com/fmtlib/fmt/issues/2676>`_).
Thanks `@sobolevn (Nikita Sobolev) <https://github.com/sobolevn>`_,
`@UnePierre (Max FERGER) <https://github.com/UnePierre>`_,
`@zhsj <https://github.com/zhsj>`_,
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
`@ericcurtin (Eric Curtin) <https://github.com/ericcurtin>`_,
`@Lounarok <https://github.com/Lounarok>`_.
* Improved fuzzers and added a fuzzer for chrono timepoint formatting
(`#2461 <https://github.com/fmtlib/fmt/pull/2461>`_,
`#2469 <https://github.com/fmtlib/fmt/pull/2469>`_).
`@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_,
* Added the ``FMT_SYSTEM_HEADERS`` CMake option setting which marks {fmt}'s
headers as system. It can be used to suppress warnings
(`#2644 <https://github.com/fmtlib/fmt/issues/2644>`_,
`#2651 <https://github.com/fmtlib/fmt/pull/2651>`_).
Thanks `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_.
* Added the Bazel build system support
(`#2505 <https://github.com/fmtlib/fmt/pull/2505>`_,
`#2516 <https://github.com/fmtlib/fmt/pull/2516>`_).
Thanks `@Vertexwahn <https://github.com/Vertexwahn>`_.
* Improved build configuration and tests
(`#2437 <https://github.com/fmtlib/fmt/issues/2437>`_,
`#2558 <https://github.com/fmtlib/fmt/pull/2558>`_,
`#2648 <https://github.com/fmtlib/fmt/pull/2648>`_,
`#2650 <https://github.com/fmtlib/fmt/pull/2650>`_,
`#2663 <https://github.com/fmtlib/fmt/pull/2663>`_,
`#2677 <https://github.com/fmtlib/fmt/pull/2677>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
`@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_,
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Fixed various warnings and compilation issues
(`#2353 <https://github.com/fmtlib/fmt/pull/2353>`_,
`#2356 <https://github.com/fmtlib/fmt/pull/2356>`_,
`#2399 <https://github.com/fmtlib/fmt/pull/2399>`_,
`#2408 <https://github.com/fmtlib/fmt/issues/2408>`_,
`#2414 <https://github.com/fmtlib/fmt/pull/2414>`_,
`#2427 <https://github.com/fmtlib/fmt/pull/2427>`_,
`#2432 <https://github.com/fmtlib/fmt/pull/2432>`_,
`#2442 <https://github.com/fmtlib/fmt/pull/2442>`_,
`#2434 <https://github.com/fmtlib/fmt/pull/2434>`_,
`#2439 <https://github.com/fmtlib/fmt/issues/2439>`_,
`#2447 <https://github.com/fmtlib/fmt/pull/2447>`_,
`#2450 <https://github.com/fmtlib/fmt/pull/2450>`_,
`#2455 <https://github.com/fmtlib/fmt/issues/2455>`_,
`#2465 <https://github.com/fmtlib/fmt/issues/2465>`_,
`#2472 <https://github.com/fmtlib/fmt/issues/2472>`_,
`#2474 <https://github.com/fmtlib/fmt/issues/2474>`_,
`#2476 <https://github.com/fmtlib/fmt/pull/2476>`_,
`#2478 <https://github.com/fmtlib/fmt/issues/2478>`_,
`#2479 <https://github.com/fmtlib/fmt/issues/2479>`_,
`#2481 <https://github.com/fmtlib/fmt/issues/2481>`_,
`#2482 <https://github.com/fmtlib/fmt/pull/2482>`_,
`#2483 <https://github.com/fmtlib/fmt/pull/2483>`_,
`#2490 <https://github.com/fmtlib/fmt/issues/2490>`_,
`#2491 <https://github.com/fmtlib/fmt/pull/2491>`_,
`#2510 <https://github.com/fmtlib/fmt/pull/2510>`_,
`#2518 <https://github.com/fmtlib/fmt/pull/2518>`_,
`#2528 <https://github.com/fmtlib/fmt/issues/2528>`_,
`#2529 <https://github.com/fmtlib/fmt/pull/2529>`_,
`#2539 <https://github.com/fmtlib/fmt/pull/2539>`_,
`#2540 <https://github.com/fmtlib/fmt/issues/2540>`_,
`#2545 <https://github.com/fmtlib/fmt/pull/2545>`_,
`#2555 <https://github.com/fmtlib/fmt/pull/2555>`_,
`#2557 <https://github.com/fmtlib/fmt/issues/2557>`_,
`#2570 <https://github.com/fmtlib/fmt/issues/2570>`_,
`#2573 <https://github.com/fmtlib/fmt/pull/2573>`_,
`#2582 <https://github.com/fmtlib/fmt/pull/2582>`_,
`#2605 <https://github.com/fmtlib/fmt/issues/2605>`_,
`#2611 <https://github.com/fmtlib/fmt/pull/2611>`_,
`#2647 <https://github.com/fmtlib/fmt/pull/2647>`_,
`#2627 <https://github.com/fmtlib/fmt/issues/2627>`_,
`#2630 <https://github.com/fmtlib/fmt/pull/2630>`_,
`#2635 <https://github.com/fmtlib/fmt/issues/2635>`_,
`#2638 <https://github.com/fmtlib/fmt/issues/2638>`_,
`#2653 <https://github.com/fmtlib/fmt/issues/2653>`_,
`#2654 <https://github.com/fmtlib/fmt/issues/2654>`_,
`#2661 <https://github.com/fmtlib/fmt/issues/2661>`_,
`#2664 <https://github.com/fmtlib/fmt/pull/2664>`_,
`#2684 <https://github.com/fmtlib/fmt/pull/2684>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
`@mwinterb <https://github.com/mwinterb>`_,
`@cdacamar (Cameron DaCamara) <https://github.com/cdacamar>`_,
`@TrebledJ (Johnathan) <https://github.com/TrebledJ>`_,
`@bodomartin (brm) <https://github.com/bodomartin>`_,
`@cquammen (Cory Quammen) <https://github.com/cquammen>`_,
`@white238 (Chris White) <https://github.com/white238>`_,
`@mmarkeloff (Max) <https://github.com/mmarkeloff>`_,
`@palacaze (Pierre-Antoine Lacaze) <https://github.com/palacaze>`_,
`@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_,
`@mborn-adi (Mathias Born) <https://github.com/mborn-adi>`_,
`@BrukerJWD (Jonathan W) <https://github.com/BrukerJWD>`_,
`@spyridon97 (Spiros Tsalikis) <https://github.com/spyridon97>`_,
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
`@oliverlee (Oliver Lee) <https://github.com/oliverlee>`_,
`@joshessman-llnl (Josh Essman) <https://github.com/joshessman-llnl>`_,
`@akohlmey (Axel Kohlmeyer) <https://github.com/akohlmey>`_,
`@timkalu <https://github.com/timkalu>`_,
`@olupton (Olli Lupton) <https://github.com/olupton>`_,
`@Acretock <https://github.com/Acretock>`_,
`@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_,
`@andrewcorrigan (Andrew Corrigan) <https://github.com/andrewcorrigan>`_,
`@lucpelletier <https://github.com/lucpelletier>`_,
`@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_.
8.0.1 - 2021-07-02 8.0.1 - 2021-07-02
------------------ ------------------
@ -34,7 +405,7 @@
`#2389 <https://github.com/fmtlib/fmt/pull/2389>`_, `#2389 <https://github.com/fmtlib/fmt/pull/2389>`_,
`#2395 <https://github.com/fmtlib/fmt/pull/2395>`_, `#2395 <https://github.com/fmtlib/fmt/pull/2395>`_,
`#2397 <https://github.com/fmtlib/fmt/pull/2397>`_, `#2397 <https://github.com/fmtlib/fmt/pull/2397>`_,
`#2400 <https://github.com/fmtlib/fmt/issues/2400>`_ `#2400 <https://github.com/fmtlib/fmt/issues/2400>`_,
`#2401 <https://github.com/fmtlib/fmt/issues/2401>`_, `#2401 <https://github.com/fmtlib/fmt/issues/2401>`_,
`#2407 <https://github.com/fmtlib/fmt/pull/2407>`_). `#2407 <https://github.com/fmtlib/fmt/pull/2407>`_).
Thanks `@zx2c4 (Jason A. Donenfeld) <https://github.com/zx2c4>`_, Thanks `@zx2c4 (Jason A. Donenfeld) <https://github.com/zx2c4>`_,
@ -50,7 +421,7 @@
8.0.0 - 2021-06-21 8.0.0 - 2021-06-21
------------------ ------------------
* Enabled compile-time format string check by default. * Enabled compile-time format string checks by default.
For example (`godbolt <https://godbolt.org/z/sMxcohGjz>`__): For example (`godbolt <https://godbolt.org/z/sMxcohGjz>`__):
.. code:: c++ .. code:: c++
@ -281,6 +652,9 @@
This doesn't introduce a dependency on ``<locale>`` so there is virtually no This doesn't introduce a dependency on ``<locale>`` so there is virtually no
compile time effect. compile time effect.
* Deprecated an undocumented ``format_to`` overload that takes
``basic_memory_buffer``.
* Made parameter order in ``vformat_to`` consistent with ``format_to`` * Made parameter order in ``vformat_to`` consistent with ``format_to``
(`#2327 <https://github.com/fmtlib/fmt/issues/2327>`_). (`#2327 <https://github.com/fmtlib/fmt/issues/2327>`_).
@ -562,13 +936,13 @@
`#2067 <https://github.com/fmtlib/fmt/pull/2067>`_, `#2067 <https://github.com/fmtlib/fmt/pull/2067>`_,
`#2068 <https://github.com/fmtlib/fmt/pull/2068>`_, `#2068 <https://github.com/fmtlib/fmt/pull/2068>`_,
`#2073 <https://github.com/fmtlib/fmt/pull/2073>`_, `#2073 <https://github.com/fmtlib/fmt/pull/2073>`_,
`#2103 <https://github.com/fmtlib/fmt/issues/2103>`_ `#2103 <https://github.com/fmtlib/fmt/issues/2103>`_,
`#2105 <https://github.com/fmtlib/fmt/issues/2105>`_ `#2105 <https://github.com/fmtlib/fmt/issues/2105>`_,
`#2106 <https://github.com/fmtlib/fmt/pull/2106>`_, `#2106 <https://github.com/fmtlib/fmt/pull/2106>`_,
`#2107 <https://github.com/fmtlib/fmt/pull/2107>`_, `#2107 <https://github.com/fmtlib/fmt/pull/2107>`_,
`#2116 <https://github.com/fmtlib/fmt/issues/2116>`_ `#2116 <https://github.com/fmtlib/fmt/issues/2116>`_,
`#2117 <https://github.com/fmtlib/fmt/pull/2117>`_, `#2117 <https://github.com/fmtlib/fmt/pull/2117>`_,
`#2118 <https://github.com/fmtlib/fmt/issues/2118>`_ `#2118 <https://github.com/fmtlib/fmt/issues/2118>`_,
`#2119 <https://github.com/fmtlib/fmt/pull/2119>`_, `#2119 <https://github.com/fmtlib/fmt/pull/2119>`_,
`#2127 <https://github.com/fmtlib/fmt/issues/2127>`_, `#2127 <https://github.com/fmtlib/fmt/issues/2127>`_,
`#2128 <https://github.com/fmtlib/fmt/pull/2128>`_, `#2128 <https://github.com/fmtlib/fmt/pull/2128>`_,
@ -641,7 +1015,7 @@
`@yeswalrus (Walter Gray) <https://github.com/yeswalrus>`_, `@yeswalrus (Walter Gray) <https://github.com/yeswalrus>`_,
`@Finkman <https://github.com/Finkman>`_, `@Finkman <https://github.com/Finkman>`_,
`@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_, `@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_,
`@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_ `@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_,
`@concatime (Issam Maghni) <https://github.com/concatime>`_, `@concatime (Issam Maghni) <https://github.com/concatime>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_, `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@summivox (Yin Zhong) <https://github.com/summivox>`_, `@summivox (Yin Zhong) <https://github.com/summivox>`_,
@ -1098,7 +1472,7 @@
`#1912 <https://github.com/fmtlib/fmt/issues/1912>`_, `#1912 <https://github.com/fmtlib/fmt/issues/1912>`_,
`#1928 <https://github.com/fmtlib/fmt/issues/1928>`_, `#1928 <https://github.com/fmtlib/fmt/issues/1928>`_,
`#1929 <https://github.com/fmtlib/fmt/pull/1929>`_, `#1929 <https://github.com/fmtlib/fmt/pull/1929>`_,
`#1935 <https://github.com/fmtlib/fmt/issues/1935>`_ `#1935 <https://github.com/fmtlib/fmt/issues/1935>`_,
`#1937 <https://github.com/fmtlib/fmt/pull/1937>`_, `#1937 <https://github.com/fmtlib/fmt/pull/1937>`_,
`#1942 <https://github.com/fmtlib/fmt/pull/1942>`_, `#1942 <https://github.com/fmtlib/fmt/pull/1942>`_,
`#1949 <https://github.com/fmtlib/fmt/issues/1949>`_). `#1949 <https://github.com/fmtlib/fmt/issues/1949>`_).

17
vendor/Fmt/README.rst vendored
View File

@ -1,5 +1,7 @@
{fmt} .. image:: https://user-images.githubusercontent.com/
===== 576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png
:width: 25%
:alt: {fmt}
.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg .. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux
@ -26,9 +28,8 @@
**{fmt}** is an open-source formatting library providing a fast and safe **{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams. alternative to C stdio and C++ iostreams.
If you like this project, please consider donating to the BYSOL If you like this project, please consider donating to one of the funds that
Foundation that helps victims of political repressions in Belarus: help victims of the war in Ukraine: https://www.stopputin.net/.
https://bysol.org/en/bs/general/.
`Documentation <https://fmt.dev>`__ `Documentation <https://fmt.dev>`__
@ -123,7 +124,7 @@ Output::
Default format: 42s 100ms Default format: 42s 100ms
strftime-like format: 03:15:30 strftime-like format: 03:15:30
**Print a container** (`run <https://godbolt.org/z/MjsY7c>`_) **Print a container** (`run <https://godbolt.org/z/MxM1YqjE7>`_)
.. code:: c++ .. code:: c++
@ -205,7 +206,7 @@ The above results were generated by building ``tinyformat_test.cpp`` on macOS
best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
further details refer to the `source further details refer to the `source
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_. <https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc>`_.
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on {fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_) floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
@ -469,7 +470,7 @@ Boost Format
This is a very powerful library which supports both ``printf``-like format This is a very powerful library which supports both ``printf``-like format
strings and positional arguments. Its main drawback is performance. According to strings and positional arguments. Its main drawback is performance. According to
various, benchmarks it is much slower than other methods considered here. Boost various benchmarks, it is much slower than other methods considered here. Boost
Format also has excessive build times and severe code bloat issues (see Format also has excessive build times and severe code bloat issues (see
`Benchmarks`_). `Benchmarks`_).

View File

@ -37,10 +37,12 @@ similar to that of Python's `str.format
<https://docs.python.org/3/library/stdtypes.html#str.format>`_. <https://docs.python.org/3/library/stdtypes.html#str.format>`_.
They take *fmt* and *args* as arguments. They take *fmt* and *args* as arguments.
*fmt* is a format string that contains literal text and replacement *fmt* is a format string that contains literal text and replacement fields
fields surrounded by braces ``{}``. The fields are replaced with formatted surrounded by braces ``{}``. The fields are replaced with formatted arguments
arguments in the resulting string. A function taking *fmt* doesn't in the resulting string. `~fmt::format_string` is a format string which can be
participate in an overload resolution if the latter is not a string. implicitly constructed from a string literal or a ``constexpr`` string and is
checked at compile time in C++20. To pass a runtime format string wrap it in
`fmt::runtime`.
*args* is an argument list representing objects to be formatted. *args* is an argument list representing objects to be formatted.
@ -50,7 +52,7 @@ participate in an overload resolution if the latter is not a string.
.. doxygenfunction:: vformat(string_view fmt, format_args args) -> std::string .. doxygenfunction:: vformat(string_view fmt, format_args args) -> std::string
.. doxygenfunction:: format_to(OutputIt out, format_string<T...> fmt, T&&... args) -> OutputIt .. doxygenfunction:: format_to(OutputIt out, format_string<T...> fmt, T&&... args) -> OutputIt
.. doxygenfunction:: format_to_n(OutputIt out, size_t n, format_string<T...> fmt, const T&... args) -> format_to_n_result<OutputIt> .. doxygenfunction:: format_to_n(OutputIt out, size_t n, format_string<T...> fmt, T&&... args) -> format_to_n_result<OutputIt>
.. doxygenfunction:: formatted_size(format_string<T...> fmt, T&&... args) -> size_t .. doxygenfunction:: formatted_size(format_string<T...> fmt, T&&... args) -> size_t
.. doxygenstruct:: fmt::format_to_n_result .. doxygenstruct:: fmt::format_to_n_result
@ -59,7 +61,7 @@ participate in an overload resolution if the latter is not a string.
.. _print: .. _print:
.. doxygenfunction:: fmt::print(format_string<T...> fmt, T&&... args) .. doxygenfunction:: fmt::print(format_string<T...> fmt, T&&... args)
.. doxygenfunction:: vprint(string_view fmt, format_args args) .. doxygenfunction:: fmt::vprint(string_view fmt, format_args args)
.. doxygenfunction:: print(std::FILE *f, format_string<T...> fmt, T&&... args) .. doxygenfunction:: print(std::FILE *f, format_string<T...> fmt, T&&... args)
.. doxygenfunction:: vprint(std::FILE *f, string_view fmt, format_args args) .. doxygenfunction:: vprint(std::FILE *f, string_view fmt, format_args args)
@ -70,6 +72,7 @@ Compile-time Format String Checks
Compile-time checks are enabled when using ``FMT_STRING``. They support built-in Compile-time checks are enabled when using ``FMT_STRING``. They support built-in
and string types as well as user-defined types with ``constexpr`` ``parse`` and string types as well as user-defined types with ``constexpr`` ``parse``
functions in their ``formatter`` specializations. functions in their ``formatter`` specializations.
Requires C++14 and is a no-op in C++11.
.. doxygendefine:: FMT_STRING .. doxygendefine:: FMT_STRING
@ -78,6 +81,13 @@ To force the use of compile-time checks, define the preprocessor variable
will fail to compile with regular strings. Runtime-checked will fail to compile with regular strings. Runtime-checked
formatting is still possible using ``fmt::vformat``, ``fmt::vprint``, etc. formatting is still possible using ``fmt::vformat``, ``fmt::vprint``, etc.
.. doxygenclass:: fmt::basic_format_string
:members:
.. doxygentypedef:: fmt::format_string
.. doxygenfunction:: fmt::runtime(const S&)
Named Arguments Named Arguments
--------------- ---------------
@ -103,8 +113,7 @@ binary footprint, for example (https://godbolt.org/z/oba4Mc):
template <typename S, typename... Args> template <typename S, typename... Args>
void log(const char* file, int line, const S& format, Args&&... args) { void log(const char* file, int line, const S& format, Args&&... args) {
vlog(file, line, format, vlog(file, line, format, fmt::make_format_args(args...));
fmt::make_args_checked<Args...>(format, args...));
} }
#define MY_LOG(format, ...) \ #define MY_LOG(format, ...) \
@ -115,8 +124,6 @@ binary footprint, for example (https://godbolt.org/z/oba4Mc):
Note that ``vlog`` is not parameterized on argument types which improves compile Note that ``vlog`` is not parameterized on argument types which improves compile
times and reduces binary code size compared to a fully parameterized version. times and reduces binary code size compared to a fully parameterized version.
.. doxygenfunction:: fmt::make_args_checked(const S&, const remove_reference_t<Args>&...)
.. doxygenfunction:: fmt::make_format_args(const Args&...) .. doxygenfunction:: fmt::make_format_args(const Args&...)
.. doxygenclass:: fmt::format_arg_store .. doxygenclass:: fmt::format_arg_store
@ -172,12 +179,19 @@ functions and locale support.
Formatting User-defined Types Formatting User-defined Types
----------------------------- -----------------------------
The {fmt} library provides formatters for many standard C++ types.
See :ref:`fmt/ranges.h <ranges-api>` for ranges and tuples including standard
containers such as ``std::vector`` and :ref:`fmt/chrono.h <chrono-api>` for date
and time formatting.
To make a user-defined type formattable, specialize the ``formatter<T>`` struct To make a user-defined type formattable, specialize the ``formatter<T>`` struct
template and implement ``parse`` and ``format`` methods:: template and implement ``parse`` and ``format`` methods::
#include <fmt/format.h> #include <fmt/format.h>
struct point { double x, y; }; struct point {
double x, y;
};
template <> struct fmt::formatter<point> { template <> struct fmt::formatter<point> {
// Presentation format: 'f' - fixed, 'e' - exponential. // Presentation format: 'f' - fixed, 'e' - exponential.
@ -201,8 +215,7 @@ template and implement ``parse`` and ``format`` methods::
if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++; if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
// Check if reached the end of the range: // Check if reached the end of the range:
if (it != end && *it != '}') if (it != end && *it != '}') throw format_error("invalid format");
throw format_error("invalid format");
// Return an iterator past the end of the parsed range: // Return an iterator past the end of the parsed range:
return it; return it;
@ -211,12 +224,11 @@ template and implement ``parse`` and ``format`` methods::
// Formats the point p using the parsed format specification (presentation) // Formats the point p using the parsed format specification (presentation)
// stored in this formatter. // stored in this formatter.
template <typename FormatContext> template <typename FormatContext>
auto format(const point& p, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const point& p, FormatContext& ctx) const -> decltype(ctx.out()) {
// ctx.out() is an output iterator to write to. // ctx.out() is an output iterator to write to.
return format_to( return presentation == 'f'
ctx.out(), ? format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y)
presentation == 'f' ? "({:.1f}, {:.1f})" : "({:.1e}, {:.1e})", : format_to(ctx.out(), "({:.1e}, {:.1e})", p.x, p.y);
p.x, p.y);
} }
}; };
@ -234,7 +246,7 @@ example::
template <> struct fmt::formatter<color>: formatter<string_view> { template <> 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) const {
string_view name = "unknown"; string_view name = "unknown";
switch (c) { switch (c) {
case color::red: name = "red"; break; case color::red: name = "red"; break;
@ -272,7 +284,7 @@ You can also write a formatter for a hierarchy of classes::
struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> : struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> :
fmt::formatter<std::string> { fmt::formatter<std::string> {
template <typename FormatCtx> template <typename FormatCtx>
auto format(const A& a, FormatCtx& ctx) { auto format(const A& a, FormatCtx& ctx) const {
return fmt::formatter<std::string>::format(a.name(), ctx); return fmt::formatter<std::string>::format(a.name(), ctx);
} }
}; };
@ -306,6 +318,8 @@ Utilities
.. doxygenfunction:: fmt::ptr(const std::unique_ptr<T> &p) -> const void* .. doxygenfunction:: fmt::ptr(const std::unique_ptr<T> &p) -> const void*
.. doxygenfunction:: fmt::ptr(const std::shared_ptr<T> &p) -> const void* .. doxygenfunction:: fmt::ptr(const std::shared_ptr<T> &p) -> const void*
.. doxygenfunction:: fmt::underlying(Enum e) -> typename std::underlying_type<Enum>::type
.. doxygenfunction:: fmt::to_string(const T &value) -> std::string .. doxygenfunction:: fmt::to_string(const T &value) -> std::string
.. doxygenfunction:: fmt::to_string_view(const Char *s) -> basic_string_view<Char> .. doxygenfunction:: fmt::to_string_view(const Char *s) -> basic_string_view<Char>
@ -314,6 +328,8 @@ Utilities
.. doxygenfunction:: fmt::join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> .. doxygenfunction:: fmt::join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel>
.. doxygenfunction:: fmt::group_digits(T value) -> group_digits_view<T>
.. doxygenclass:: fmt::detail::buffer .. doxygenclass:: fmt::detail::buffer
:members: :members:
@ -434,16 +450,19 @@ The format syntax is described in :ref:`chrono-specs`.
Format string compilation Format string compilation
========================= =========================
``fmt/compile.h`` provides format string compilation support when using ``fmt/compile.h`` provides format string compilation enabled via the
``FMT_COMPILE``. Format strings are parsed, checked and converted into efficient ``FMT_COMPILE`` macro or the ``_cf`` user-defined literal. Format strings
formatting code at compile-time. This supports arguments of built-in and string marked with ``FMT_COMPILE`` or ``_cf`` are parsed, checked and converted into
types as well as user-defined types with ``constexpr`` ``parse`` functions in efficient formatting code at compile-time. This supports arguments of built-in
their ``formatter`` specializations. Format string compilation can generate more and string types as well as user-defined types with ``constexpr`` ``parse``
binary code compared to the default API and is only recommended in places where functions in their ``formatter`` specializations. Format string compilation can
formatting is a performance bottleneck. generate more binary code compared to the default API and is only recommended in
places where formatting is a performance bottleneck.
.. doxygendefine:: FMT_COMPILE .. doxygendefine:: FMT_COMPILE
.. doxygenfunction:: operator""_cf()
.. _color-api: .. _color-api:
Terminal color and text style Terminal color and text style
@ -457,6 +476,8 @@ Terminal color and text style
.. doxygenfunction:: bg(detail::color_type) .. doxygenfunction:: bg(detail::color_type)
.. doxygenfunction:: styled(const T& value, text_style ts)
.. _os-api: .. _os-api:
System APIs System APIs
@ -474,7 +495,9 @@ System APIs
======================== ========================
``fmt/ostream.h`` provides ``std::ostream`` support including formatting of ``fmt/ostream.h`` provides ``std::ostream`` support including formatting of
user-defined types that have an overloaded insertion operator (``operator<<``):: user-defined types that have an overloaded insertion operator (``operator<<``).
In order to make a type formattable via ``std::ostream`` you should provide a
``formatter`` specialization inherited from ``ostream_formatter``::
#include <fmt/ostream.h> #include <fmt/ostream.h>
@ -488,12 +511,11 @@ user-defined types that have an overloaded insertion operator (``operator<<``)::
} }
}; };
template <> struct fmt::formatter<date> : ostream_formatter {};
std::string s = fmt::format("The date is {}", date(2012, 12, 9)); std::string s = fmt::format("The date is {}", date(2012, 12, 9));
// s == "The date is 2012-12-9" // s == "The date is 2012-12-9"
{fmt} only supports insertion operators that are defined in the same namespaces
as the types they format and can be found with the argument-dependent lookup.
.. doxygenfunction:: print(std::basic_ostream<Char> &os, const S &format_str, Args&&... args) .. doxygenfunction:: print(std::basic_ostream<Char> &os, const S &format_str, Args&&... args)
.. _printf-api: .. _printf-api:
@ -519,8 +541,8 @@ argument type doesn't match its format specification.
``wchar_t`` Support ``wchar_t`` Support
=================== ===================
The optional header ``fmt/wchar_t.h`` provides support for ``wchar_t`` and The optional header ``fmt/xchar.h`` provides support for ``wchar_t`` and exotic
exotic character types. character types.
.. doxygenstruct:: fmt::is_char .. doxygenstruct:: fmt::is_char

View File

@ -90,12 +90,14 @@
VERSION: '{{ release|e }}', VERSION: '{{ release|e }}',
COLLAPSE_INDEX: false, COLLAPSE_INDEX: false,
FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}', FILE_SUFFIX: '{{ '' if no_search_suffix else file_suffix }}',
LINK_SUFFIX: '{{ link_suffix }}',
SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}',
HAS_SOURCE: {{ has_source|lower }}, HAS_SOURCE: {{ has_source|lower }},
SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}' SOURCELINK_SUFFIX: '{{ sourcelink_suffix }}'
}; };
</script> </script>
{%- for scriptfile in script_files %} {%- for scriptfile in script_files %}
<script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script> {{ js_tag(scriptfile) }}
{%- endfor %} {%- endfor %}
{%- endmacro %} {%- endmacro %}

View File

@ -4,7 +4,7 @@
import errno, os, re, sys import errno, os, re, sys
from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2', '7.1.3', '8.0.0', '8.0.1'] versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2', '7.1.3', '8.0.0', '8.0.1', '8.1.0', '8.1.1']
class Pip: class Pip:
def __init__(self, venv_dir): def __init__(self, venv_dir):
@ -26,6 +26,8 @@ def create_build_env(venv_dir='virtualenv'):
pip = Pip(venv_dir) pip = Pip(venv_dir)
pip.install('wheel') pip.install('wheel')
pip.install('six') pip.install('six')
# See: https://github.com/sphinx-doc/sphinx/issues/9777
pip.install('docutils==0.17.1')
pip.install('sphinx-doc/sphinx', 'v3.3.0') pip.install('sphinx-doc/sphinx', 'v3.3.0')
pip.install('michaeljones/breathe', 'v4.25.0') pip.install('michaeljones/breathe', 'v4.25.0')
@ -58,10 +60,12 @@ def build_docs(version='dev', **kwargs):
MACRO_EXPANSION = YES MACRO_EXPANSION = YES
PREDEFINED = _WIN32=1 \ PREDEFINED = _WIN32=1 \
__linux__=1 \ __linux__=1 \
FMT_ENABLE_IF(...)= \
FMT_USE_VARIADIC_TEMPLATES=1 \ FMT_USE_VARIADIC_TEMPLATES=1 \
FMT_USE_RVALUE_REFERENCES=1 \ FMT_USE_RVALUE_REFERENCES=1 \
FMT_USE_USER_DEFINED_LITERALS=1 \ FMT_USE_USER_DEFINED_LITERALS=1 \
FMT_USE_ALIAS_TEMPLATES=1 \ FMT_USE_ALIAS_TEMPLATES=1 \
FMT_USE_NONTYPE_TEMPLATE_PARAMETERS=1 \
FMT_API= \ FMT_API= \
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \ "FMT_BEGIN_NAMESPACE=namespace fmt {{" \
"FMT_END_NAMESPACE=}}" \ "FMT_END_NAMESPACE=}}" \

View File

@ -101,7 +101,7 @@ The code
format(FMT_STRING("The answer is {:d}"), "forty-two"); format(FMT_STRING("The answer is {:d}"), "forty-two");
reports a compile-time error on compilers that support relaxed ``constexpr``. reports a compile-time error on compilers that support relaxed ``constexpr``.
See `here <api.html#c.fmt>`_ for details. See `here <api.html#compile-time-format-string-checks>`_ for details.
The following code The following code

View File

@ -112,18 +112,18 @@ meaning in this case.
The *sign* option is only valid for number types, and can be one of the The *sign* option is only valid for number types, and can be one of the
following: following:
+---------+----------------------------------------------------------+ +---------+------------------------------------------------------------+
| Option | Meaning | | Option | Meaning |
+=========+==========================================================+ +=========+============================================================+
| ``'+'`` | indicates that a sign should be used for both | | ``'+'`` | indicates that a sign should be used for both |
| | positive as well as negative numbers. | | | nonnegative as well as negative numbers. |
+---------+----------------------------------------------------------+ +---------+------------------------------------------------------------+
| ``'-'`` | indicates that a sign should be used only for negative | | ``'-'`` | indicates that a sign should be used only for negative |
| | numbers (this is the default behavior). | | | numbers (this is the default behavior). |
+---------+----------------------------------------------------------+ +---------+------------------------------------------------------------+
| space | indicates that a leading space should be used on | | space | indicates that a leading space should be used on |
| | positive numbers, and a minus sign on negative numbers. | | | nonnegative numbers, and a minus sign on negative numbers. |
+---------+----------------------------------------------------------+ +---------+------------------------------------------------------------+
The ``'#'`` option causes the "alternate form" to be used for the The ``'#'`` option causes the "alternate form" to be used for the
conversion. The alternate form is defined differently for different conversion. The alternate form is defined differently for different
@ -161,7 +161,8 @@ displayed after the decimal point for a floating-point value formatted with
value formatted with ``'g'`` or ``'G'``. For non-number types the field value formatted with ``'g'`` or ``'G'``. For non-number types the field
indicates the maximum field size - in other words, how many characters will be indicates the maximum field size - in other words, how many characters will be
used from the field content. The *precision* is not allowed for integer, used from the field content. The *precision* is not allowed for integer,
character, Boolean, and pointer values. character, Boolean, and pointer values. Note that a C string must be
null-terminated even if precision is specified.
The ``'L'`` option uses the current locale setting to insert the appropriate The ``'L'`` option uses the current locale setting to insert the appropriate
number separator characters. This option is only valid for numeric types. number separator characters. This option is only valid for numeric types.
@ -303,7 +304,8 @@ The available presentation types for pointers are:
Chrono Format Specifications Chrono Format Specifications
============================ ============================
Format specifications for chrono types have the following syntax: Format specifications for chrono types and ``std::tm`` have the following
syntax:
.. productionlist:: sf .. productionlist:: sf
chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`] chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`]
@ -347,9 +349,35 @@ points are:
Specifiers that have a calendaric component such as `'d'` (the day of month) Specifiers that have a calendaric component such as `'d'` (the day of month)
are valid only for ``std::tm`` and not durations or time points. are valid only for ``std::tm`` and not durations or time points.
``std::tm`` uses the system's `strftime .. range-specs:
<https://en.cppreference.com/w/cpp/chrono/c/strftime>`_ so refer to its
documentation for details on supported conversion specifiers. Range Format Specifications
===========================
Format specifications for range types have the following syntax:
..productionlist:: sf
range_format_spec: [":" [`underlying_spec`]]
The `underlying_spec` is parsed based on the formatter of the range's
reference type.
By default, a range of characters or strings is printed escaped and quoted. But
if any `underlying_spec` is provided (even if it is empty), then the characters
or strings are printed according to the provided specification.
Examples:
fmt::format("{}", std::vector{10, 20, 30});
// Result: [10, 20, 30]
fmt::format("{::#x}", std::vector{10, 20, 30});
// Result: [0xa, 0x14, 0x13]
fmt::format("{}", vector{'h', 'e', 'l', 'l', 'o'});
// Result: ['h', 'e', 'l', 'l', 'o']
fmt::format("{::}", vector{'h', 'e', 'l', 'l', 'o'});
// Result: [h, e, l, l, o]
fmt::format("{::d}", vector{'h', 'e', 'l', 'l', 'o'});
// Result: [104, 101, 108, 108, 111]
.. _formatexamples: .. _formatexamples:
@ -449,7 +477,7 @@ Using type-specific formatting::
Using the comma as a thousands separator:: Using the comma as a thousands separator::
#include <fmt/locale.h> #include <fmt/format.h>
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890); auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
// s == "1,234,567,890" // s == "1,234,567,890"

View File

@ -95,10 +95,10 @@ class dynamic_format_arg_store
}; };
template <typename T> template <typename T>
using stored_type = conditional_t<detail::is_string<T>::value && using stored_type = conditional_t<
!has_formatter<T, Context>::value && std::is_convertible<T, std::basic_string<char_type>>::value &&
!detail::is_reference_wrapper<T>::value, !detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>; std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous. // Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_; std::vector<basic_format_arg<Context>> data_;
@ -145,16 +145,6 @@ class dynamic_format_arg_store
public: public:
constexpr dynamic_format_arg_store() = default; constexpr dynamic_format_arg_store() = default;
constexpr dynamic_format_arg_store(
const dynamic_format_arg_store<Context>& store)
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(),
#endif
data_(store.data_),
named_info_(store.named_info_) {
}
/** /**
\rst \rst
Adds an argument into the dynamic store for later passing to a formatting Adds an argument into the dynamic store for later passing to a formatting

File diff suppressed because it is too large Load Diff

View File

@ -214,17 +214,16 @@ FMT_BEGIN_DETAIL_NAMESPACE
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {} FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true), FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color); value.rgb_color = static_cast<uint32_t>(rgb_color);
} }
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} { FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) | value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b; (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
} }
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(), FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
value{} { : is_rgb(), value{} {
value.term_color = static_cast<uint8_t>(term_color); value.term_color = static_cast<uint8_t>(term_color);
} }
bool is_rgb; bool is_rgb;
@ -239,10 +238,8 @@ FMT_END_DETAIL_NAMESPACE
/** A text style consisting of foreground and background colors and emphasis. */ /** A text style consisting of foreground and background colors and emphasis. */
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: set_foreground_color(), : set_foreground_color(), set_background_color(), ems(em) {}
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) { FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
if (!set_foreground_color) { if (!set_foreground_color) {
@ -283,34 +280,32 @@ class text_style {
return lhs.and_assign(rhs); return lhs.and_assign(rhs);
} }
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_foreground() const noexcept {
return set_foreground_color; return set_foreground_color;
} }
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_background() const noexcept {
return set_background_color; return set_background_color;
} }
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_emphasis() const noexcept {
return static_cast<uint8_t>(ems) != 0; return static_cast<uint8_t>(ems) != 0;
} }
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
FMT_ASSERT(has_foreground(), "no foreground specified for this style"); FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color; return foreground_color;
} }
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT { FMT_CONSTEXPR detail::color_type get_background() const noexcept {
FMT_ASSERT(has_background(), "no background specified for this style"); FMT_ASSERT(has_background(), "no background specified for this style");
return background_color; return background_color;
} }
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems; return ems;
} }
private: private:
FMT_CONSTEXPR text_style(bool is_foreground, FMT_CONSTEXPR text_style(bool is_foreground,
detail::color_type text_color) FMT_NOEXCEPT detail::color_type text_color) noexcept
: set_foreground_color(), : set_foreground_color(), set_background_color(), ems() {
set_background_color(),
ems() {
if (is_foreground) { if (is_foreground) {
foreground_color = text_color; foreground_color = text_color;
set_foreground_color = true; set_foreground_color = true;
@ -345,11 +340,11 @@ class text_style {
return *this; return *this;
} }
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) friend FMT_CONSTEXPR_DECL text_style
FMT_NOEXCEPT; fg(detail::color_type foreground) noexcept;
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) friend FMT_CONSTEXPR_DECL text_style
FMT_NOEXCEPT; bg(detail::color_type background) noexcept;
detail::color_type foreground_color; detail::color_type foreground_color;
detail::color_type background_color; detail::color_type background_color;
@ -359,17 +354,16 @@ class text_style {
}; };
/** Creates a text style from the foreground (text) color. */ /** Creates a text style from the foreground (text) color. */
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT { FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
return text_style(true, foreground); return text_style(true, foreground);
} }
/** Creates a text style from the background color. */ /** Creates a text style from the background color. */
FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT { FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
return text_style(false, background); return text_style(false, background);
} }
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs; return text_style(lhs) | rhs;
} }
@ -377,7 +371,7 @@ FMT_BEGIN_DETAIL_NAMESPACE
template <typename Char> struct ansi_color_escape { template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
const char* esc) FMT_NOEXCEPT { const char* esc) noexcept {
// If we have a terminal color, we need to output another escape code // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb) {
@ -412,7 +406,7 @@ template <typename Char> struct ansi_color_escape {
to_esc(color.b, buffer + 15, 'm'); to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0); buffer[19] = static_cast<Char>(0);
} }
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
uint8_t em_codes[num_emphases] = {}; uint8_t em_codes[num_emphases] = {};
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
@ -433,10 +427,10 @@ template <typename Char> struct ansi_color_escape {
} }
buffer[index++] = static_cast<Char>(0); buffer[index++] = static_cast<Char>(0);
} }
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT { FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
return buffer + std::char_traits<Char>::length(buffer); return buffer + std::char_traits<Char>::length(buffer);
} }
@ -445,59 +439,64 @@ template <typename Char> struct ansi_color_escape {
Char buffer[7u + 3u * num_emphases + 1u]; Char buffer[7u + 3u * num_emphases + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT { char delimiter) noexcept {
out[0] = static_cast<Char>('0' + c / 100); out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10); out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10); out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter); out[3] = static_cast<Char>(delimiter);
} }
static FMT_CONSTEXPR bool has_emphasis(emphasis em, static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
emphasis mask) FMT_NOEXCEPT {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask); return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
} }
}; };
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) FMT_NOEXCEPT { detail::color_type foreground) noexcept {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;"); return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
detail::color_type background) FMT_NOEXCEPT { detail::color_type background) noexcept {
return ansi_color_escape<Char>(background, "\x1b[48;2;"); return ansi_color_escape<Char>(background, "\x1b[48;2;");
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
return ansi_color_escape<Char>(em); return ansi_color_escape<Char>(em);
} }
template <typename Char> template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT { int result = std::fputs(chars, stream);
std::fputs(chars, stream); if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
template <> template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT { int result = std::fputws(chars, stream);
std::fputws(chars, stream); if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT { template <typename Char> inline void reset_color(FILE* stream) {
fputs("\x1b[0m", stream); fputs("\x1b[0m", stream);
} }
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT { template <> inline void reset_color<wchar_t>(FILE* stream) {
fputs(L"\x1b[0m", stream); fputs(L"\x1b[0m", stream);
} }
template <typename Char> template <typename Char> inline void reset_color(buffer<Char>& buffer) {
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
auto reset_color = string_view("\x1b[0m"); auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end()); buffer.append(reset_color.begin(), reset_color.end());
} }
template <typename T> struct styled_arg {
const T& value;
text_style style;
};
template <typename Char> template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts, void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
@ -529,8 +528,12 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args); detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0)); if (detail::is_utf8()) {
detail::fputs(buf.data(), f); detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
} else {
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
} }
/** /**
@ -549,7 +552,7 @@ template <typename S, typename... Args,
void print(std::FILE* f, const text_style& ts, const S& format_str, void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
vprint(f, ts, format_str, vprint(f, ts, format_str,
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<char_t<S>>>(args...));
} }
/** /**
@ -594,7 +597,7 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
return fmt::vformat(ts, to_string_view(format_str), return fmt::vformat(ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
/** /**
@ -629,7 +632,60 @@ inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) -> Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str), return vformat_to(out, ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<char_t<S>>>(args...));
}
template <typename T, typename Char>
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
template <typename FormatContext>
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-> decltype(ctx.out()) {
const auto& ts = arg.style;
const auto& value = arg.value;
auto out = ctx.out();
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
out = std::copy(emphasis.begin(), emphasis.end(), out);
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
detail::make_foreground_color<Char>(ts.get_foreground());
out = std::copy(foreground.begin(), foreground.end(), out);
}
if (ts.has_background()) {
has_style = true;
auto background =
detail::make_background_color<Char>(ts.get_background());
out = std::copy(background.begin(), background.end(), out);
}
out = formatter<T, Char>::format(value, ctx);
if (has_style) {
auto reset_color = string_view("\x1b[0m");
out = std::copy(reset_color.begin(), reset_color.end(), out);
}
return out;
}
};
/**
\rst
Returns an argument that will be formatted using ANSI escape sequences,
to be used in a formatting function.
**Example**::
fmt::print("Elapsed time: {s:.2f} seconds",
fmt::styled(1.23, fmt::fg(fmt::colors::green) | fmt::bg(fmt::color::blue)));
\endrst
*/
template <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
-> detail::styled_arg<remove_cvref_t<T>> {
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
} }
FMT_MODULE_EXPORT_END FMT_MODULE_EXPORT_END

View File

@ -13,45 +13,6 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
// An output iterator that counts the number of objects written to it and
// discards them.
class counting_iterator {
private:
size_t count_;
public:
using iterator_category = std::output_iterator_tag;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type = counting_iterator; // Mark iterator as checked.
struct value_type {
template <typename T> void operator=(const T&) {}
};
counting_iterator() : count_(0) {}
size_t count() const { return count_; }
counting_iterator& operator++() {
++count_;
return *this;
}
counting_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
friend counting_iterator operator+(counting_iterator it, difference_type n) {
it.count_ += static_cast<size_t>(n);
return it;
}
value_type operator*() const { return {}; }
};
template <typename Char, typename InputIt> template <typename Char, typename InputIt>
inline counting_iterator copy_str(InputIt begin, InputIt end, inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) { counting_iterator it) {
@ -156,7 +117,7 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
std::string s = fmt::format(FMT_COMPILE("{}"), 42); std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst \endrst
*/ */
#ifdef __cpp_if_constexpr #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) \ # define FMT_COMPILE(s) \
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
#else #else
@ -168,7 +129,7 @@ template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str> fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string { struct udl_compiled_string : compiled_string {
using char_type = Char; using char_type = Char;
constexpr operator basic_string_view<char_type>() const { explicit constexpr operator basic_string_view<char_type>() const {
return {Str.data, N - 1}; return {Str.data, N - 1};
} }
}; };
@ -179,7 +140,7 @@ const T& first(const T& value, const Tail&...) {
return value; return value;
} }
#ifdef __cpp_if_constexpr #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename... Args> struct type_list {}; template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...]. // Returns a reference to the argument at index N from [first, rest...].
@ -190,7 +151,7 @@ constexpr const auto& get([[maybe_unused]] const T& first,
if constexpr (N == 0) if constexpr (N == 0)
return first; return first;
else else
return get<N - 1>(rest...); return detail::get<N - 1>(rest...);
} }
template <typename Char, typename... Args> template <typename Char, typename... Args>
@ -202,7 +163,8 @@ constexpr int get_arg_index_by_name(basic_string_view<Char> name,
template <int N, typename> struct get_type_impl; template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> { template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>; using type =
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
}; };
template <int N, typename T> template <int N, typename T>
@ -242,7 +204,7 @@ template <typename Char> struct code_unit {
// This ensures that the argument type is convertible to `const T&`. // This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args> template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) { constexpr const T& get_arg_checked(const Args&... args) {
const auto& arg = get<N>(args...); const auto& arg = detail::get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) { if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value; return arg.value;
} else { } else {
@ -399,7 +361,9 @@ template <typename Char> struct arg_id_handler {
return 0; return 0;
} }
constexpr void on_error(const char* message) { FMT_THROW(format_error(message)); } constexpr void on_error(const char* message) {
FMT_THROW(format_error(message));
}
}; };
template <typename Char> struct parse_arg_id_result { template <typename Char> struct parse_arg_id_result {
@ -527,12 +491,12 @@ constexpr auto compile(S format_str) {
return result; return result;
} }
} }
#endif // __cpp_if_constexpr #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT_BEGIN FMT_MODULE_EXPORT_BEGIN
#ifdef __cpp_if_constexpr #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename CompiledFormat, typename... Args, template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type, typename Char = typename CompiledFormat::char_type,
@ -570,10 +534,11 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
constexpr auto compiled = detail::compile<Args...>(S()); constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) { detail::unknown_format>()) {
return format(static_cast<basic_string_view<typename S::char_type>>(S()), return fmt::format(
std::forward<Args>(args)...); static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else { } else {
return format(compiled, std::forward<Args>(args)...); return fmt::format(compiled, std::forward<Args>(args)...);
} }
} }
@ -583,11 +548,11 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S()); constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) { detail::unknown_format>()) {
return format_to(out, return fmt::format_to(
static_cast<basic_string_view<typename S::char_type>>(S()), out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...); std::forward<Args>(args)...);
} else { } else {
return format_to(out, compiled, std::forward<Args>(args)...); return fmt::format_to(out, compiled, std::forward<Args>(args)...);
} }
} }
#endif #endif
@ -596,22 +561,23 @@ template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const S& format_str, Args&&... args) { const S& format_str, Args&&... args) {
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str, auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
std::forward<Args>(args)...); format_str, std::forward<Args>(args)...);
return {it.base(), it.count()}; return {it.base(), it.count()};
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
size_t formatted_size(const S& format_str, const Args&... args) { size_t formatted_size(const S& format_str, const Args&... args) {
return format_to(detail::counting_iterator(), format_str, args...).count(); return fmt::format_to(detail::counting_iterator(), format_str, args...)
.count();
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) { void print(std::FILE* f, const S& format_str, const Args&... args) {
memory_buffer buffer; memory_buffer buffer;
format_to(std::back_inserter(buffer), format_str, args...); fmt::format_to(std::back_inserter(buffer), format_str, args...);
detail::print(f, {buffer.data(), buffer.size()}); detail::print(f, {buffer.data(), buffer.size()});
} }
@ -623,12 +589,10 @@ void print(const S& format_str, const Args&... args) {
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
inline namespace literals { inline namespace literals {
template <detail_exported::fixed_string Str> template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
constexpr detail::udl_compiled_string< using char_t = remove_cvref_t<decltype(Str.data[0])>;
remove_cvref_t<decltype(Str.data[0])>, return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> Str>();
operator""_cf() {
return {};
} }
} // namespace literals } // namespace literals
#endif #endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -9,10 +9,8 @@
#define FMT_OS_H_ #define FMT_OS_H_
#include <cerrno> #include <cerrno>
#include <clocale> // locale_t
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstdlib> // strtod_l
#include <system_error> // std::system_error #include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__) #if defined __APPLE__ || defined(__FreeBSD__)
@ -21,17 +19,20 @@
#include "format.h" #include "format.h"
#ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe. // UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h") # if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h> # include <winapifamily.h>
#endif # endif
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \ # if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \ defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) (!defined(WINAPI_FAMILY) || \
# include <fcntl.h> // for O_RDONLY (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# define FMT_USE_FCNTL 1 # include <fcntl.h> // for O_RDONLY
#else # define FMT_USE_FCNTL 1
# define FMT_USE_FCNTL 0 # else
# define FMT_USE_FCNTL 0
# endif
#endif #endif
#ifndef FMT_POSIX #ifndef FMT_POSIX
@ -138,7 +139,7 @@ template <typename Char> struct formatter<std::error_code, Char> {
}; };
#ifdef _WIN32 #ifdef _WIN32
FMT_API const std::error_category& system_category() FMT_NOEXCEPT; FMT_API const std::error_category& system_category() noexcept;
FMT_BEGIN_DETAIL_NAMESPACE FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8. // A converter from UTF-16 to UTF-8.
@ -162,7 +163,7 @@ class utf16_to_utf8 {
}; };
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) FMT_NOEXCEPT; const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
FMT_API std::system_error vwindows_error(int error_code, string_view format_str, FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
@ -204,10 +205,9 @@ std::system_error windows_error(int error_code, string_view message,
// Reports a Windows error without throwing an exception. // Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors. // Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, FMT_API void report_windows_error(int error_code, const char* message) noexcept;
const char* message) FMT_NOEXCEPT;
#else #else
inline const std::error_category& system_category() FMT_NOEXCEPT { inline const std::error_category& system_category() noexcept {
return std::system_category(); return std::system_category();
} }
#endif // _WIN32 #endif // _WIN32
@ -234,13 +234,13 @@ class buffered_file {
void operator=(const buffered_file&) = delete; void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file. // Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {} buffered_file() noexcept : file_(nullptr) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT; FMT_API ~buffered_file() noexcept;
public: public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr; other.file_ = nullptr;
} }
@ -258,10 +258,11 @@ class buffered_file {
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; } FILE* get() const noexcept { return file_; }
// We place parentheses around fileno to workaround a bug in some versions // We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro. // of MinGW that define fileno as a macro.
// DEPRECATED! Rename to descriptor to avoid issues with macros.
FMT_API int(fileno)() const; FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) { void vprint(string_view format_str, format_args args) {
@ -276,12 +277,12 @@ class buffered_file {
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1. // A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw // Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as // fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather // closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the // than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler. // invalid parameter handler with _set_invalid_parameter_handler.
class file { class FMT_API file {
private: private:
int fd_; // File descriptor. int fd_; // File descriptor.
@ -300,16 +301,16 @@ class file {
}; };
// Constructs a file object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {} file() noexcept : fd_(-1) {}
// Opens a file and constructs a file object representing this file. // Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag); file(cstring_view path, int oflag);
public: public:
file(const file&) = delete; file(const file&) = delete;
void operator=(const file&) = delete; void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw. // Move assignment is not noexcept because close may throw.
file& operator=(file&& other) { file& operator=(file&& other) {
@ -320,43 +321,43 @@ class file {
} }
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT; ~file() noexcept;
// Returns the file descriptor. // Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; } int descriptor() const noexcept { return fd_; }
// Closes the file. // Closes the file.
FMT_API void close(); void close();
// Returns the file size. The size has signed type for consistency with // Returns the file size. The size has signed type for consistency with
// stat::st_size. // stat::st_size.
FMT_API long long size() const; long long size() const;
// Attempts to read count bytes from the file into the specified buffer. // Attempts to read count bytes from the file into the specified buffer.
FMT_API size_t read(void* buffer, size_t count); size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file. // Attempts to write count bytes from the specified buffer to the file.
FMT_API size_t write(const void* buffer, size_t count); size_t write(const void* buffer, size_t count);
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
FMT_API static file dup(int fd); static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd); void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT; void dup2(int fd, std::error_code& ec) noexcept;
// Creates a pipe setting up read_end and write_end file objects for reading // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end); static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // this file object from the file.
FMT_API buffered_file fdopen(const char* mode); buffered_file fdopen(const char* mode);
}; };
// Returns the memory page size. // Returns the memory page size.
@ -390,6 +391,13 @@ struct ostream_params {
: ostream_params(params...) { : ostream_params(params...) {
this->buffer_size = bs.value; this->buffer_size = bs.value;
} }
// Intel has a bug that results in failure to deduce a constructor
// for empty parameter packs.
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
ostream_params(int new_oflag) : oflag(new_oflag) {}
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
# endif
}; };
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
@ -452,7 +460,7 @@ class FMT_API ostream final : private detail::buffer<char> {
* ``<integer>``: Flags passed to `open * ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ <https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE`` by default) (``file::WRONLY | file::CREATE | file::TRUNC`` by default)
* ``buffer_size=<integer>``: Output buffer size * ``buffer_size=<integer>``: Output buffer size
**Example**:: **Example**::
@ -467,50 +475,6 @@ inline ostream output_file(cstring_view path, T... params) {
} }
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
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"));
}
~locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_MODULE_EXPORT_END FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -14,73 +14,20 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context; template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail { namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> { // Checks if T has a user-defined operator<<.
private: template <typename T, typename Char, typename Enable = void>
using int_type = typename std::basic_streambuf<Char>::int_type; class is_streamable {
using traits_type = typename std::basic_streambuf<Char>::traits_type;
buffer<Char>& buffer_;
public:
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
struct converter {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
void_t<> operator<<(converter);
};
// Hide insertion operators for built-in types.
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
private: private:
template <typename U> template <typename U>
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>() static auto test(int)
<< std::declval<U>()), -> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
void_t<>>::value> << std::declval<U>()) != 0>;
test(int);
template <typename> static std::false_type test(...); template <typename> static auto test(...) -> std::false_type;
using result = decltype(test<T>(0)); using result = decltype(test<T>(0));
@ -90,7 +37,21 @@ template <typename T, typename Char> class is_streamable {
static const bool value = result::value; static const bool value = result::value;
}; };
// Formatting of built-in types and arrays is intentionally disabled because
// it's handled by standard (non-ostream) formatters.
template <typename T, typename Char>
struct is_streamable<
T, Char,
enable_if_t<
std::is_arithmetic<T>::value || std::is_array<T>::value ||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
std::is_same<T, std_string_view<Char>>::value ||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
: std::false_type {};
// Write the content of buf to os. // Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing.
template <typename Char> template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data(); const Char* buf_data = buf.data();
@ -108,8 +69,8 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
template <typename Char, typename T> template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value, void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) { locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf); auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
std::basic_ostream<Char> output(&format_buf); auto&& output = std::basic_ostream<Char>(&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
@ -117,34 +78,35 @@ void format_value(buffer<Char>& buf, const T& value,
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.try_resize(buf.size()); buf.try_resize(buf.size());
} }
} // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
template <typename T, typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt {
auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale());
return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx);
}
};
using ostream_formatter = basic_ostream_formatter<char>;
namespace detail {
// 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>>
: private formatter<basic_string_view<Char>, Char> { : basic_ostream_formatter<Char> {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) using basic_ostream_formatter<Char>::format;
-> decltype(ctx.begin()) { // DEPRECATED!
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> template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) const
-> OutputIt { -> OutputIt {
basic_memory_buffer<Char> buffer; auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
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()); format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out()); return std::copy(buffer.begin(), buffer.end(), ctx.out());
} }
@ -155,7 +117,7 @@ FMT_MODULE_EXPORT
template <typename Char> template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; auto buffer = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args); detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer); detail::write_buffer(os, buffer);
} }
@ -174,7 +136,7 @@ template <typename S, typename... Args,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str), vprint(os, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -233,7 +233,7 @@ class printf_arg_formatter : public arg_formatter<Char> {
OutputIt write_null_pointer(bool is_string = false) { OutputIt write_null_pointer(bool is_string = false) {
auto s = this->specs; auto s = this->specs;
s.type = 0; s.type = presentation_type::none;
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s); return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
} }
@ -249,8 +249,10 @@ class printf_arg_formatter : public arg_formatter<Char> {
// std::is_same instead. // std::is_same instead.
if (std::is_same<T, Char>::value) { if (std::is_same<T, Char>::value) {
format_specs fmt_specs = this->specs; format_specs fmt_specs = this->specs;
if (fmt_specs.type && fmt_specs.type != 'c') if (fmt_specs.type != presentation_type::none &&
fmt_specs.type != presentation_type::chr) {
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
}
fmt_specs.sign = sign::none; fmt_specs.sign = sign::none;
fmt_specs.alt = false; fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
@ -271,13 +273,13 @@ class printf_arg_formatter : public arg_formatter<Char> {
/** Formats a null-terminated C string. */ /** Formats a null-terminated C string. */
OutputIt operator()(const char* value) { OutputIt operator()(const char* value) {
if (value) return base::operator()(value); if (value) return base::operator()(value);
return write_null_pointer(this->specs.type != 'p'); return write_null_pointer(this->specs.type != presentation_type::pointer);
} }
/** Formats a null-terminated wide C string. */ /** Formats a null-terminated wide C string. */
OutputIt operator()(const wchar_t* value) { OutputIt operator()(const wchar_t* value) {
if (value) return base::operator()(value); if (value) return base::operator()(value);
return write_null_pointer(this->specs.type != 'p'); return write_null_pointer(this->specs.type != presentation_type::pointer);
} }
OutputIt operator()(basic_string_view<Char> value) { OutputIt operator()(basic_string_view<Char> value) {
@ -490,13 +492,13 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
// Parse type. // Parse type.
if (it == end) FMT_THROW(format_error("invalid format string")); if (it == end) FMT_THROW(format_error("invalid format string"));
specs.type = static_cast<char>(*it++); char type = static_cast<char>(*it++);
if (arg.is_integral()) { if (arg.is_integral()) {
// Normalize type. // Normalize type.
switch (specs.type) { switch (type) {
case 'i': case 'i':
case 'u': case 'u':
specs.type = 'd'; type = 'd';
break; break;
case 'c': case 'c':
visit_format_arg( visit_format_arg(
@ -505,6 +507,9 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
break; break;
} }
} }
specs.type = parse_presentation_type(type);
if (specs.type == presentation_type::none)
parse_ctx.on_error("invalid type specifier");
start = it; start = it;

View File

@ -13,37 +13,13 @@
#define FMT_RANGES_H_ #define FMT_RANGES_H_
#include <initializer_list> #include <initializer_list>
#include <tuple>
#include <type_traits> #include <type_traits>
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename Char, typename Enable = void> struct formatting_range {
#ifdef FMT_DEPRECATED_BRACED_RANGES
Char prefix = '{';
Char postfix = '}';
#else
Char prefix = '[';
Char postfix = ']';
#endif
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
template <typename Char, typename Enable = void> struct formatting_tuple {
Char prefix = '(';
Char postfix = ')';
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
namespace detail { namespace detail {
template <typename RangeT, typename OutputIterator> template <typename RangeT, typename OutputIterator>
@ -71,7 +47,7 @@ OutputIterator copy(wchar_t ch, OutputIterator out) {
return out; return out;
} }
/// Return true value if T has std::string interface, like std::string_view. // Returns true if T has a std::string-like interface, like std::string_view.
template <typename T> class is_std_string_like { template <typename T> class is_std_string_like {
template <typename U> template <typename U>
static auto check(U* p) static auto check(U* p)
@ -80,12 +56,40 @@ template <typename T> class is_std_string_like {
public: public:
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value; is_string<T>::value ||
std::is_convertible<T, std_string_view<char>>::value ||
!std::is_void<decltype(check<T>(nullptr))>::value;
}; };
template <typename Char> template <typename Char>
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {}; struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
template <typename T> class is_map {
template <typename U> static auto check(U*) -> typename U::mapped_type;
template <typename> static void check(...);
public:
#ifdef FMT_FORMAT_MAP_AS_LIST
static FMT_CONSTEXPR_DECL const bool value = false;
#else
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
#endif
};
template <typename T> class is_set {
template <typename U> static auto check(U*) -> typename U::key_type;
template <typename> static void check(...);
public:
#ifdef FMT_FORMAT_SET_AS_LIST
static FMT_CONSTEXPR_DECL const bool value = false;
#else
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
#endif
};
template <typename... Ts> struct conditional_helper {}; template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {}; template <typename T, typename _ = void> struct is_range_ : std::false_type {};
@ -163,7 +167,7 @@ struct is_range_<T, void>
# undef FMT_DECLTYPE_RETURN # undef FMT_DECLTYPE_RETURN
#endif #endif
/// 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) -> decltype(std::tuple_size<U>::value, int()); static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
@ -199,7 +203,7 @@ using make_index_sequence = make_integer_sequence<size_t, N>;
#endif #endif
template <class Tuple, class F, size_t... Is> template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT { void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept {
using std::get; using std::get;
// using free function get<I>(T) now. // using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
@ -217,9 +221,28 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
} }
#if FMT_MSC_VER
// Older MSVC doesn't get the reference type correctly for arrays.
template <typename R> struct range_reference_type_impl {
using type = decltype(*detail::range_begin(std::declval<R&>()));
};
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
using type = T&;
};
template <typename T>
using range_reference_type = typename range_reference_type_impl<T>::type;
#else
template <typename Range> template <typename Range>
using value_type = using range_reference_type =
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>; decltype(*detail::range_begin(std::declval<Range&>()));
#endif
// We don't use the Range's value_type for anything, but we do need the Range's
// reference type, with cv-ref stripped.
template <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) { template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
*out++ = ','; *out++ = ',';
@ -227,283 +250,22 @@ template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
return out; return out;
} }
struct singleton {
unsigned char upper;
unsigned char lowercount;
};
inline auto check(uint16_t x, const singleton* singletonuppers,
size_t singletonuppers_size,
const unsigned char* singletonlowers,
const unsigned char* normal, size_t normal_size) -> bool {
auto xupper = x >> 8;
auto lowerstart = 0;
for (size_t i = 0; i < singletonuppers_size; ++i) {
auto su = singletonuppers[i];
auto lowerend = lowerstart + su.lowercount;
if (xupper < su.upper) break;
if (xupper == su.upper) {
for (auto j = lowerstart; j < lowerend; ++j) {
if (singletonlowers[j] == x) return false;
}
}
lowerstart = lowerend;
}
auto xsigned = static_cast<int>(x);
auto current = true;
for (size_t i = 0; i < normal_size; ++i) {
auto v = static_cast<int>(normal[i]);
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[i++] : v;
xsigned -= len;
if (xsigned < 0) break;
current = !current;
}
return current;
}
// Returns true iff the code point cp is printable.
// This code is generated by support/printable.py.
inline auto is_printable(uint32_t cp) -> bool {
static constexpr singleton singletons0u[] = {
{0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8},
{0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
{0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5},
{0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22},
{0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3},
{0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8},
{0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9},
};
static constexpr unsigned char singletons0l[] = {
0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
0xfe, 0xff,
};
static constexpr singleton singletons1u[] = {
{0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2},
{0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5},
{0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5},
{0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2},
{0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5},
{0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2},
{0xfa, 2}, {0xfb, 1},
};
static constexpr unsigned char singletons1l[] = {
0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
};
static constexpr unsigned char normal0[] = {
0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
};
static constexpr unsigned char normal1[] = {
0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
};
auto lower = static_cast<uint16_t>(cp);
if (cp < 0x10000) {
return check(lower, singletons0u,
sizeof(singletons0u) / sizeof(*singletons0u), singletons0l,
normal0, sizeof(normal0));
}
if (cp < 0x20000) {
return check(lower, singletons1u,
sizeof(singletons1u) / sizeof(*singletons1u), singletons1l,
normal1, sizeof(normal1));
}
if (0x2a6de <= cp && cp < 0x2a700) return false;
if (0x2b735 <= cp && cp < 0x2b740) return false;
if (0x2b81e <= cp && cp < 0x2b820) return false;
if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
if (0x2ebe1 <= cp && cp < 0x2f800) return false;
if (0x2fa1e <= cp && cp < 0x30000) return false;
if (0x3134b <= cp && cp < 0xe0100) return false;
if (0xe01f0 <= cp && cp < 0x110000) return false;
return true;
}
inline auto needs_escape(uint32_t cp) -> bool {
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
!is_printable(cp);
}
template <typename Char> struct find_escape_result {
const Char* begin;
const Char* end;
uint32_t cp;
};
template <typename Char>
auto find_escape(const Char* begin, const Char* end)
-> find_escape_result<Char> {
for (; begin != end; ++begin) {
auto cp = static_cast<typename std::make_unsigned<Char>::type>(*begin);
if (needs_escape(cp)) return {begin, begin + 1, cp};
}
return {begin, nullptr, 0};
}
auto find_escape(const char* begin, const char* end)
-> find_escape_result<char> {
if (!is_utf8()) return find_escape<char>(begin, end);
auto result = find_escape_result<char>{end, nullptr, 0};
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
[&](uint32_t cp, string_view sv) {
if (needs_escape(cp)) {
result = {sv.begin(), sv.end(), cp};
return false;
}
return true;
});
return result;
}
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt { auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
*out++ = '"'; return write_escaped_string(out, str);
auto begin = str.begin(), end = str.end(); }
do {
auto escape = find_escape(begin, end); template <typename Char, typename OutputIt, typename T,
out = copy_str<Char>(begin, escape.begin, out); FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
begin = escape.end; inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
if (!begin) break; auto sv = std_string_view<Char>(str);
auto c = static_cast<Char>(escape.cp); return write_range_entry<Char>(out, basic_string_view<Char>(sv));
switch (escape.cp) {
case '\n':
*out++ = '\\';
c = 'n';
break;
case '\r':
*out++ = '\\';
c = 'r';
break;
case '\t':
*out++ = '\\';
c = 't';
break;
case '"':
FMT_FALLTHROUGH;
case '\\':
*out++ = '\\';
break;
default:
if (is_utf8() && escape.cp > 0xffff) {
out = format_to(out, "\\U{:08x}", escape.cp);
continue;
}
for (Char escape_char : basic_string_view<Char>(
escape.begin, to_unsigned(escape.end - escape.begin))) {
out = format_to(
out, "\\x{:02x}",
static_cast<typename std::make_unsigned<Char>::type>(escape_char));
}
continue;
}
*out++ = c;
} while (begin != end);
*out++ = '"';
return out;
} }
template <typename Char, typename OutputIt, typename Arg, template <typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)> FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg v) { OutputIt write_range_entry(OutputIt out, const Arg v) {
*out++ = '\''; return write_escaped_char(out, v);
*out++ = v;
*out++ = '\'';
return out;
} }
template < template <
@ -524,63 +286,153 @@ template <typename T> struct is_tuple_like {
template <typename TupleT, typename Char> template <typename TupleT, typename Char>
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> { struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
private: private:
// C++11 generic lambda for format() // C++11 generic lambda for format().
template <typename FormatContext> struct format_each { template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) { template <typename T> void operator()(const T& v) {
if (i > 0) out = detail::write_delimiter(out); if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, v); out = detail::write_range_entry<Char>(out, v);
++i; ++i;
} }
formatting_tuple<Char>& formatting; int i;
size_t& i; typename FormatContext::iterator& out;
typename std::add_lvalue_reference<
decltype(std::declval<FormatContext>().out())>::type out;
}; };
public: public:
formatting_tuple<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return ctx.begin();
} }
template <typename FormatContext = format_context> template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const TupleT& values, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
size_t i = 0; *out++ = '(';
detail::for_each(values, format_each<FormatContext>{0, out});
detail::copy(formatting.prefix, out); *out++ = ')';
detail::for_each(values, format_each<FormatContext>{formatting, i, out}); return out;
detail::copy(formatting.postfix, out);
return ctx.out();
} }
}; };
template <typename T, typename Char> struct is_range { template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value && detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
!detail::is_map<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value && !std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<detail::std_string_view<Char>, T>::value; !std::is_constructible<detail::std_string_view<Char>, T>::value;
}; };
namespace detail {
template <typename Context> struct range_mapper {
using mapper = arg_mapper<Context>;
template <typename T,
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value) -> T&& {
return static_cast<T&&>(value);
}
template <typename T,
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value)
-> decltype(mapper().map(static_cast<T&&>(value))) {
return mapper().map(static_cast<T&&>(value));
}
};
template <typename Char, typename Element>
using range_formatter_type = conditional_t<
is_formattable<Element, Char>::value,
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
std::declval<Element>()))>,
Char>,
fallback_formatter<Element, Char>>;
template <typename R>
using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>;
} // namespace detail
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<
fmt::is_range<R, Char>::value
// Workaround a bug in MSVC 2019 and earlier.
#if !FMT_MSC_VER
&&
(is_formattable<detail::uncvref_type<detail::maybe_const_range<R>>,
Char>::value ||
detail::has_fallback_formatter<
detail::uncvref_type<detail::maybe_const_range<R>>, Char>::value)
#endif
>> {
using range_type = detail::maybe_const_range<R>;
using formatter_type =
detail::range_formatter_type<Char, detail::uncvref_type<range_type>>;
formatter_type underlying_;
bool custom_specs_ = false;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it != ':')
FMT_THROW(format_error("no top-level range formatters supported"));
custom_specs_ = true;
++it;
ctx.advance_to(it);
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
#ifdef FMT_DEPRECATED_BRACED_RANGES
Char prefix = '{';
Char postfix = '}';
#else
Char prefix = detail::is_set<R>::value ? '{' : '[';
Char postfix = detail::is_set<R>::value ? '}' : ']';
#endif
detail::range_mapper<buffer_context<Char>> mapper;
auto out = ctx.out();
*out++ = prefix;
int i = 0;
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
for (; it != end; ++it) {
if (i > 0) out = detail::write_delimiter(out);
if (custom_specs_) {
ctx.advance_to(out);
out = underlying_.format(mapper.map(*it), ctx);
} else {
out = detail::write_range_entry<Char>(out, *it);
}
++i;
}
*out++ = postfix;
return out;
}
};
template <typename T, typename Char> template <typename T, typename Char>
struct formatter< struct formatter<
T, Char, T, Char,
enable_if_t< enable_if_t<detail::is_map<T>::value
fmt::is_range<T, Char>::value // Workaround a bug in MSVC 2019 and earlier.
// Workaround a bug in MSVC 2017 and earlier. #if !FMT_MSC_VER
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927 && (is_formattable<detail::uncvref_type<T>, Char>::value ||
&& (has_formatter<detail::value_type<T>, format_context>::value || detail::has_fallback_formatter<detail::uncvref_type<T>,
detail::has_fallback_formatter<detail::value_type<T>, Char>::value) Char>::value)
#endif #endif
>> { >> {
formatting_range<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return ctx.begin();
} }
template < template <
@ -588,17 +440,20 @@ struct formatter<
FMT_ENABLE_IF( FMT_ENABLE_IF(
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value, std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
const T, T>>::value)> const T, T>>::value)>
auto format(U& values, FormatContext& ctx) -> decltype(ctx.out()) { auto format(U& map, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = detail::copy(formatting.prefix, ctx.out()); auto out = ctx.out();
size_t i = 0; *out++ = '{';
auto it = std::begin(values); int i = 0;
auto end = std::end(values); for (const auto& item : map) {
for (; it != end; ++it) {
if (i > 0) out = detail::write_delimiter(out); if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, *it); out = detail::write_range_entry<Char>(out, item.first);
*out++ = ':';
*out++ = ' ';
out = detail::write_range_entry<Char>(out, item.second);
++i; ++i;
} }
return detail::copy(formatting.postfix, out); *out++ = '}';
return out;
} }
}; };
@ -613,46 +468,70 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view {
template <typename Char, typename... T> template <typename Char, typename... T>
using tuple_arg_join = tuple_join_view<Char, T...>; using tuple_arg_join = tuple_join_view<Char, T...>;
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
// support in tuple_join. It is disabled by default because of issues with
// the dynamic width and precision.
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename... T> template <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> { struct formatter<tuple_join_view<Char, T...>, Char> {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx) -> auto format(const tuple_join_view<Char, T...>& value,
typename FormatContext::iterator { FormatContext& ctx) const -> typename FormatContext::iterator {
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{}); return do_format(value, ctx,
std::integral_constant<size_t, sizeof...(T)>());
} }
private: private:
template <typename FormatContext, size_t... N> std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
detail::index_sequence<N...>) -> template <typename ParseContext>
typename FormatContext::iterator { FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
using std::get; std::integral_constant<size_t, 0>)
return format_args(value, ctx, get<N>(value.tuple)...); -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename ParseContext, size_t N>
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
std::integral_constant<size_t, N>)
-> decltype(ctx.begin()) {
auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
FMT_THROW(format_error("incompatible format specs for tuple elements"));
}
#endif
return end;
} }
template <typename FormatContext> template <typename FormatContext>
auto format_args(const tuple_join_view<Char, T...>&, FormatContext& ctx) -> auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator { typename FormatContext::iterator {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out(); return ctx.out();
} }
template <typename FormatContext, typename Arg, typename... Args> template <typename FormatContext, size_t N>
auto format_args(const tuple_join_view<Char, T...>& value, FormatContext& ctx, auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) -> std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator { typename FormatContext::iterator {
using base = formatter<typename std::decay<Arg>::type, Char>; auto out = std::get<sizeof...(T) - N>(formatters_)
auto out = base().format(arg, ctx); .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
if (sizeof...(Args) > 0) { if (N > 1) {
out = std::copy(value.sep.begin(), value.sep.end(), out); out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out); ctx.advance_to(out);
return format_args(value, ctx, args...); return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
} }
return out; return out;
} }

View File

@ -5,8 +5,8 @@
// //
// For the license information refer to format.h. // For the license information refer to format.h.
#ifndef FMT_WCHAR_H_ #ifndef FMT_XCHAR_H_
#define FMT_WCHAR_H_ #define FMT_XCHAR_H_
#include <cwchar> #include <cwchar>
#include <tuple> #include <tuple>
@ -92,8 +92,8 @@ auto vformat(basic_string_view<Char> format_str,
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)> FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); return vformat(to_string_view(format_str),
return vformat(to_string_view(format_str), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename Locale, typename S, typename Char = char_t<S>, template <typename Locale, typename S, typename Char = char_t<S>,
@ -113,7 +113,7 @@ template <typename Locale, typename S, typename... Args,
inline auto format(const Locale& loc, const S& format_str, Args&&... args) inline auto format(const Locale& loc, const S& format_str, Args&&... args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename OutputIt, typename S, typename Char = char_t<S>, template <typename OutputIt, typename S, typename Char = char_t<S>,
@ -132,8 +132,8 @@ template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); return vformat_to(out, to_string_view(fmt),
return vformat_to(out, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename S, typename... Args, typename Char, size_t SIZE, template <typename S, typename... Args, typename Char, size_t SIZE,
@ -141,8 +141,8 @@ template <typename S, typename... Args, typename Char, size_t SIZE,
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf, FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
const S& format_str, Args&&... args) -> const S& format_str, Args&&... args) ->
typename buffer_context<Char>::iterator { typename buffer_context<Char>::iterator {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); detail::vformat_to(buf, to_string_view(format_str),
detail::vformat_to(buf, to_string_view(format_str), vargs, {}); fmt::make_format_args<buffer_context<Char>>(args...), {});
return detail::buffer_appender<Char>(buf); return detail::buffer_appender<Char>(buf);
} }
@ -167,8 +167,8 @@ template <
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) -> Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); return vformat_to(out, loc, to_string_view(format_str),
return vformat_to(out, loc, to_string_view(format_str), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename OutputIt, typename Char, typename... Args, template <typename OutputIt, typename Char, typename... Args,
@ -190,16 +190,16 @@ template <typename OutputIt, typename S, typename... Args,
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
const Args&... args) -> format_to_n_result<OutputIt> { const Args&... args) -> format_to_n_result<OutputIt> {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); return vformat_to_n(out, n, to_string_view(fmt),
return vformat_to_n(out, n, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
detail::counting_buffer<Char> buf; detail::counting_buffer<Char> buf;
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); detail::vformat_to(buf, to_string_view(fmt),
detail::vformat_to(buf, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
return buf.count(); return buf.count();
} }
@ -217,11 +217,11 @@ inline void vprint(wstring_view fmt, wformat_args args) {
template <typename... T> template <typename... T>
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) { void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return vprint(f, wstring_view(fmt), make_wformat_args(args...)); return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
} }
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) { template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), make_wformat_args(args...)); return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
} }
/** /**
@ -233,4 +233,4 @@ template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
FMT_MODULE_EXPORT_END FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_WCHAR_H_ #endif // FMT_XCHAR_H_

View File

@ -79,7 +79,6 @@ export module fmt;
#define FMT_END_DETAIL_NAMESPACE \ #define FMT_END_DETAIL_NAMESPACE \
} \ } \
export { export {
// all library-provided declarations and definitions // all library-provided declarations and definitions
// must be in the module purview to be exported // must be in the module purview to be exported
#include "fmt/args.h" #include "fmt/args.h"

View File

@ -10,6 +10,52 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
// DEPRECATED!
template <typename T = void> struct basic_data {
FMT_API static constexpr const char digits[100][2] = {
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'},
{'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'},
{'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'},
{'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'},
{'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'},
{'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'},
{'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'},
{'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'},
{'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'},
{'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'},
{'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'},
{'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'},
{'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
{'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
FMT_API static constexpr const char hex_digits[] = "0123456789abcdef";
FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '};
FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1,
0};
FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1,
0};
FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
0x1000000u | ' '};
};
#ifdef FMT_SHARED
// Required for -flto, -fivisibility=hidden and -shared to work
extern template struct basic_data<void>;
#endif
#if __cplusplus < 201703L
// DEPRECATED! These are here only for ABI compatiblity.
template <typename T> constexpr const char basic_data<T>::digits[][2];
template <typename T> constexpr const char basic_data<T>::hex_digits[];
template <typename T> constexpr const char basic_data<T>::signs[];
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
template <typename T>
constexpr const char basic_data<T>::right_padding_shifts[];
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
#endif
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) {
@ -24,10 +70,10 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
: snprintf_ptr(buf, size, format, precision, value); : snprintf_ptr(buf, size, format, precision, value);
} }
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x) template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(
FMT_NOEXCEPT; float x) noexcept;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x) template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(
FMT_NOEXCEPT; double x) noexcept;
} // namespace detail } // namespace detail
// Workaround a bug in MSVC2013 that prevents instantiation of format_float. // Workaround a bug in MSVC2013 that prevents instantiation of format_float.

44
vendor/Fmt/src/os.cc vendored
View File

@ -26,19 +26,23 @@
# endif # endif
# include <io.h> # include <io.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR # ifndef S_IRUSR
# define S_IRUSR _S_IREAD # define S_IRUSR _S_IREAD
# endif # endif
# ifndef S_IWUSR # ifndef S_IWUSR
# define S_IWUSR _S_IWRITE # define S_IWUSR _S_IWRITE
# endif # endif
# ifndef S_IRGRP
# ifdef __MINGW32__ # define S_IRGRP 0
# define _SH_DENYNO 0x40 # endif
# ifndef S_IWGRP
# define S_IWGRP 0
# endif
# ifndef S_IROTH
# define S_IROTH 0
# endif
# ifndef S_IWOTH
# define S_IWOTH 0
# endif # endif
# endif // _WIN32 # endif // _WIN32
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
@ -109,7 +113,7 @@ class system_message {
unsigned long result_; unsigned long result_;
wchar_t* message_; wchar_t* message_;
static bool is_whitespace(wchar_t c) FMT_NOEXCEPT { static bool is_whitespace(wchar_t c) noexcept {
return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0'; return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
} }
@ -128,15 +132,15 @@ class system_message {
} }
} }
~system_message() { LocalFree(message_); } ~system_message() { LocalFree(message_); }
explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; } explicit operator bool() const noexcept { return result_ != 0; }
operator basic_string_view<wchar_t>() const FMT_NOEXCEPT { operator basic_string_view<wchar_t>() const noexcept {
return basic_string_view<wchar_t>(message_, result_); return basic_string_view<wchar_t>(message_, result_);
} }
}; };
class utf8_system_category final : public std::error_category { class utf8_system_category final : public std::error_category {
public: public:
const char* name() const FMT_NOEXCEPT override { return "system"; } const char* name() const noexcept override { return "system"; }
std::string message(int error_code) const override { std::string message(int error_code) const override {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
@ -151,7 +155,7 @@ class utf8_system_category final : public std::error_category {
} // namespace detail } // namespace detail
FMT_API const std::error_category& system_category() FMT_NOEXCEPT { FMT_API const std::error_category& system_category() noexcept {
static const detail::utf8_system_category category; static const detail::utf8_system_category category;
return category; return category;
} }
@ -163,7 +167,7 @@ std::system_error vwindows_error(int err_code, string_view format_str,
} }
void detail::format_windows_error(detail::buffer<char>& out, int error_code, void detail::format_windows_error(detail::buffer<char>& out, int error_code,
const char* message) FMT_NOEXCEPT { const char* message) noexcept {
FMT_TRY { FMT_TRY {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
@ -178,12 +182,12 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT { void report_windows_error(int error_code, const char* message) noexcept {
report_error(detail::format_windows_error, error_code, message); report_error(detail::format_windows_error, error_code, message);
} }
#endif // _WIN32 #endif // _WIN32
buffered_file::~buffered_file() FMT_NOEXCEPT { buffered_file::~buffered_file() noexcept {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0) if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file"); report_system_error(errno, "cannot close file");
} }
@ -213,7 +217,11 @@ int buffered_file::fileno() const {
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) { file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR; # ifdef _WIN32
using mode_t = int;
# endif
constexpr mode_t mode =
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
# if defined(_WIN32) && !defined(__MINGW32__) # if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1; fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
@ -224,7 +232,7 @@ file::file(cstring_view path, int oflag) {
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
} }
file::~file() FMT_NOEXCEPT { file::~file() noexcept {
// Don't retry close in case of EINTR! // Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
@ -298,7 +306,7 @@ void file::dup2(int fd) {
} }
} }
void file::dup2(int fd, std::error_code& ec) FMT_NOEXCEPT { void file::dup2(int fd, std::error_code& ec) noexcept {
int result = 0; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) ec = std::error_code(errno, std::generic_category()); if (result == -1) ec = std::error_code(errno, std::generic_category());

29
vendor/Fmt/support/bazel/BUILD.bazel vendored Normal file
View File

@ -0,0 +1,29 @@
cc_library(
name = "fmt",
srcs = [
#"src/fmt.cc", # No C++ module support
"src/format.cc",
"src/os.cc",
],
hdrs = [
"include/fmt/args.h",
"include/fmt/chrono.h",
"include/fmt/color.h",
"include/fmt/compile.h",
"include/fmt/core.h",
"include/fmt/format.h",
"include/fmt/format-inl.h",
"include/fmt/locale.h",
"include/fmt/os.h",
"include/fmt/ostream.h",
"include/fmt/printf.h",
"include/fmt/ranges.h",
"include/fmt/xchar.h",
],
includes = [
"include",
"src",
],
strip_include_prefix = "include",
visibility = ["//visibility:public"],
)

73
vendor/Fmt/support/bazel/README.md vendored Normal file
View File

@ -0,0 +1,73 @@
# Bazel support
To get [Bazel](https://bazel.build/) working with {fmt} you can copy the files `BUILD.bazel`, `WORKSPACE.bazel`, `.bazelrc`, and `.bazelversion` from this folder (`support/bazel`) to the root folder of this project. This way {fmt} gets bazelized and can be used with Bazel (e.g. doing a `bazel build //...` on {fmt}).
## Using {fmt} as a dependency
The following minimal example shows how to use {fmt} as a dependency within a Bazel project.
The following file structure is assumed:
```
example
├── BUILD.bazel
├── main.cpp
└── WORKSPACE.bazel
```
*main.cpp*:
```c++
#include "fmt/core.h"
int main() {
fmt::print("The answer is {}\n", 42);
}
```
The expected output of this example is `The answer is 42`.
*WORKSPACE.bazel*:
```python
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "fmt",
branch = "master",
remote = "https://github.com/fmtlib/fmt",
patch_cmds = [
"mv support/bazel/.bazelrc .bazelrc",
"mv support/bazel/.bazelversion .bazelversion",
"mv support/bazel/BUILD.bazel BUILD.bazel",
"mv support/bazel/WORKSPACE.bazel WORKSPACE.bazel",
],
# Windows-related patch commands are only needed in the case MSYS2 is not installed.
# More details about the installation process of MSYS2 on Windows systems can be found here:
# https://docs.bazel.build/versions/main/install-windows.html#installing-compilers-and-language-runtimes
# Even if MSYS2 is installed the Windows related patch commands can still be used.
patch_cmds_win = [
"Move-Item -Path support/bazel/.bazelrc -Destination .bazelrc",
"Move-Item -Path support/bazel/.bazelversion -Destination .bazelversion",
"Move-Item -Path support/bazel/BUILD.bazel -Destination BUILD.bazel",
"Move-Item -Path support/bazel/WORKSPACE.bazel -Destination WORKSPACE.bazel",
],
)
```
In the *WORKSPACE* file, the {fmt} GitHub repository is fetched. Using the attribute `patch_cmds` the files `BUILD.bazel`, `WORKSPACE.bazel`, `.bazelrc`, and `.bazelversion` are moved to the root of the {fmt} repository. This way the {fmt} repository is recognized as a bazelized workspace.
*BUILD.bazel*:
```python
cc_binary(
name = "Demo",
srcs = ["main.cpp"],
deps = ["@fmt"],
)
```
The *BUILD* file defines a binary named `Demo` that has a dependency to {fmt}.
To execute the binary you can run `bazel run //:Demo`.

View File

@ -0,0 +1 @@
workspace(name = "fmt")

View File

@ -240,7 +240,7 @@ def release(args):
# Update the version in the changelog. # Update the version in the changelog.
title_len = 0 title_len = 0
for line in fileinput.input(changelog_path, inplace=True): for line in fileinput.input(changelog_path, inplace=True):
if line.decode('utf-8').startswith(version + ' - TBD'): if line.startswith(version + ' - TBD'):
line = version + ' - ' + datetime.date.today().isoformat() line = version + ' - ' + datetime.date.today().isoformat()
title_len = len(line) title_len = len(line)
line += '\n' line += '\n'
@ -270,9 +270,9 @@ def release(args):
# Create a release on GitHub. # Create a release on GitHub.
fmt_repo.push('origin', 'release') fmt_repo.push('origin', 'release')
params = {'access_token': os.getenv('FMT_TOKEN')} auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')}
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases', r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
params=params, headers=auth_headers,
data=json.dumps({'tag_name': version, data=json.dumps({'tag_name': version,
'target_commitish': 'release', 'target_commitish': 'release',
'body': changes, 'draft': True})) 'body': changes, 'draft': True}))
@ -283,8 +283,8 @@ def release(args):
package = 'fmt-{}.zip'.format(version) package = 'fmt-{}.zip'.format(version)
r = requests.post( r = requests.post(
'{}/{}/assets?name={}'.format(uploads_url, id, package), '{}/{}/assets?name={}'.format(uploads_url, id, package),
headers={'Content-Type': 'application/zip'}, headers={'Content-Type': 'application/zip'} | auth_headers,
params=params, data=open('build/fmt/' + package, 'rb')) data=open('build/fmt/' + package, 'rb'))
if r.status_code != 201: if r.status_code != 201:
raise Exception('Failed to upload an asset ' + str(r)) raise Exception('Failed to upload an asset ' + str(r))

View File

@ -171,66 +171,31 @@ def main():
normal1 = compress_normal(normal1) normal1 = compress_normal(normal1)
print("""\ print("""\
struct singleton { FMT_FUNC auto is_printable(uint32_t cp) -> bool {\
unsigned char upper;
unsigned char lowercount;
};
inline auto check(uint16_t x, const singleton* singletonuppers,
size_t singletonuppers_size,
const unsigned char* singletonlowers,
const unsigned char* normal, size_t normal_size) -> bool {
auto xupper = x >> 8;
auto lowerstart = 0;
for (size_t i = 0; i < singletonuppers_size; ++i) {
auto su = singletonuppers[i];
auto lowerend = lowerstart + su.lowercount;
if (xupper < su.upper) break;
if (xupper == su.upper) {
for (auto j = lowerstart; j < lowerend; ++j) {
if (singletonlowers[j] == x) return false;
}
}
lowerstart = lowerend;
}
auto xsigned = static_cast<int>(x);
auto current = true;
for (size_t i = 0; i < normal_size; ++i) {
auto v = static_cast<int>(normal[i]);
auto len = v & 0x80 != 0 ? (v & 0x7f) << 8 | normal[i++] : v;
xsigned -= len;
if (xsigned < 0) break;
current = !current;
}
return current;
}
inline auto is_printable(uint32_t cp) -> bool {\
""") """)
print_singletons(singletons0u, singletons0l, 'singletons0u', 'singletons0l') print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower')
print_singletons(singletons1u, singletons1l, 'singletons1u', 'singletons1l') print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower')
print_normal(normal0, 'normal0') print_normal(normal0, 'normal0')
print_normal(normal1, 'normal1') print_normal(normal1, 'normal1')
print("""\ print("""\
auto lower = static_cast<uint16_t>(cp); auto lower = static_cast<uint16_t>(cp);
if (cp < 0x10000) { if (cp < 0x10000) {
return check(lower, singletons0u, return is_printable(lower, singletons0,
sizeof(singletons0u) / sizeof(*singletons0u), singletons0l, sizeof(singletons0) / sizeof(*singletons0),
normal0, sizeof(normal0)); singletons0_lower, normal0, sizeof(normal0));
} }
if (cp < 0x20000) { if (cp < 0x20000) {
return check(lower, singletons1u, return is_printable(lower, singletons1,
sizeof(singletons1u) / sizeof(*singletons1u), singletons1l, sizeof(singletons1) / sizeof(*singletons1),
normal1, sizeof(normal1)); singletons1_lower, normal1, sizeof(normal1));
}\ }\
""") """)
for a, b in extra: for a, b in extra:
print(" if (0x{:x} <= cp && cp < 0x{:x}) return false;".format(a, a + b)) print(" if (0x{:x} <= cp && cp < 0x{:x}) return false;".format(a, a + b))
print("""\ print("""\
return true; return cp < 0x{:x};
}\ }}\
""") """.format(NUM_CODEPOINTS))
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View File

@ -4,23 +4,10 @@ set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc)
add_library(test-main STATIC ${TEST_MAIN_SRC}) add_library(test-main STATIC ${TEST_MAIN_SRC})
target_include_directories(test-main PUBLIC target_include_directories(test-main PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>) $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
target_link_libraries(test-main gtest) target_link_libraries(test-main gtest fmt)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
# Workaround GTest bug https://github.com/google/googletest/issues/705.
check_cxx_compiler_flag(
-fno-delete-null-pointer-checks HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
if (HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
target_compile_options(test-main PUBLIC -fno-delete-null-pointer-checks)
endif ()
# Use less strict pedantic flags for the tests because GMock doesn't compile
# cleanly with -pedantic and -std=c++98.
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
#set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wno-long-long -Wno-variadic-macros)
endif ()
function(add_fmt_executable name) function(add_fmt_executable name)
add_executable(${name} ${ARGN}) add_executable(${name} ${ARGN})
if (MINGW) if (MINGW)
@ -84,8 +71,13 @@ if (NOT (MSVC AND BUILD_SHARED_LIBS))
endif () endif ()
add_fmt_test(ostream-test) add_fmt_test(ostream-test)
add_fmt_test(compile-test) add_fmt_test(compile-test)
add_fmt_test(compile-fp-test HEADER_ONLY)
if (MSVC)
# Without this option, MSVC returns 199711L for the __cplusplus macro.
target_compile_options(compile-fp-test PRIVATE /Zc:__cplusplus)
endif()
add_fmt_test(printf-test) add_fmt_test(printf-test)
add_fmt_test(ranges-test) add_fmt_test(ranges-test ranges-odr-test.cc)
add_fmt_test(scan-test) add_fmt_test(scan-test)
add_fmt_test(unicode-test HEADER_ONLY) add_fmt_test(unicode-test HEADER_ONLY)
if (MSVC) if (MSVC)
@ -136,29 +128,20 @@ if (NOT MSVC_STATIC_RUNTIME)
if (FMT_PEDANTIC) if (FMT_PEDANTIC)
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif () endif ()
if (HAVE_STRTOD_L)
target_compile_definitions(posix-mock-test PRIVATE FMT_LOCALE)
endif ()
add_test(NAME posix-mock-test COMMAND posix-mock-test) add_test(NAME posix-mock-test COMMAND posix-mock-test)
add_fmt_test(os-test) add_fmt_test(os-test)
endif () endif ()
message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}") message(STATUS "FMT_PEDANTIC: ${FMT_PEDANTIC}")
if (FMT_PEDANTIC AND CXX_STANDARD LESS 20) if (FMT_PEDANTIC)
# MSVC fails to compile GMock when C++17 is enabled.
if (FMT_HAS_VARIANT AND NOT MSVC)
add_fmt_test(std-format-test)
set_property(TARGET std-format-test PROPERTY CXX_STANDARD 17)
endif ()
# Test that the library can be compiled with exceptions disabled. # Test that the library can be compiled with exceptions disabled.
# -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822. # -fno-exception is broken in icc: https://github.com/fmtlib/fmt/issues/822.
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel") if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG) check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
endif () endif ()
if (HAVE_FNO_EXCEPTIONS_FLAG) if (HAVE_FNO_EXCEPTIONS_FLAG)
add_library(noexception-test ../src/format.cc) add_library(noexception-test ../src/format.cc noexception-test.cc)
target_include_directories( target_include_directories(
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include) noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_options(noexception-test PRIVATE -fno-exceptions) target_compile_options(noexception-test PRIVATE -fno-exceptions)
@ -173,7 +156,11 @@ if (FMT_PEDANTIC AND CXX_STANDARD LESS 20)
nolocale-test PRIVATE ${PROJECT_SOURCE_DIR}/include) nolocale-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_definitions( target_compile_definitions(
nolocale-test PRIVATE FMT_STATIC_THOUSANDS_SEPARATOR=1) nolocale-test PRIVATE FMT_STATIC_THOUSANDS_SEPARATOR=1)
endif ()
# These tests are disabled on Windows because they take too long.
if (FMT_PEDANTIC AND NOT WIN32)
# Test if incorrect API usages produce compilation error.
add_test(compile-error-test ${CMAKE_CTEST_COMMAND} add_test(compile-error-test ${CMAKE_CTEST_COMMAND}
--build-and-test --build-and-test
"${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test" "${CMAKE_CURRENT_SOURCE_DIR}/compile-error-test"
@ -182,14 +169,11 @@ if (FMT_PEDANTIC AND CXX_STANDARD LESS 20)
--build-makeprogram ${CMAKE_MAKE_PROGRAM} --build-makeprogram ${CMAKE_MAKE_PROGRAM}
--build-options --build-options
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
"-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}" "-DFMT_DIR=${CMAKE_SOURCE_DIR}"
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}") "-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
endif ()
# These tests are disabled on Windows because they take too long.
if (FMT_PEDANTIC AND NOT WIN32)
# Test if the targets are found from the build directory. # Test if the targets are found from the build directory.
add_test(find-package-test ${CMAKE_CTEST_COMMAND} add_test(find-package-test ${CMAKE_CTEST_COMMAND}
-C ${CMAKE_BUILD_TYPE} -C ${CMAKE_BUILD_TYPE}

View File

@ -7,10 +7,12 @@
#include "fmt/args.h" #include "fmt/args.h"
#include <memory>
#include "gtest/gtest.h" #include "gtest/gtest.h"
TEST(args_test, basic) { TEST(args_test, basic) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42); store.push_back(42);
store.push_back("abc1"); store.push_back("abc1");
store.push_back(1.5f); store.push_back(1.5f);
@ -19,7 +21,7 @@ TEST(args_test, basic) {
TEST(args_test, strings_and_refs) { TEST(args_test, strings_and_refs) {
// Unfortunately the tests are compiled with old ABI so strings use COW. // Unfortunately the tests are compiled with old ABI so strings use COW.
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); fmt::dynamic_format_arg_store<fmt::format_context> store;
char str[] = "1234567890"; char str[] = "1234567890";
store.push_back(str); store.push_back(str);
store.push_back(std::cref(str)); store.push_back(std::cref(str));
@ -48,7 +50,7 @@ template <> struct formatter<custom_type> {
FMT_END_NAMESPACE FMT_END_NAMESPACE
TEST(args_test, custom_format) { TEST(args_test, custom_format) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); fmt::dynamic_format_arg_store<fmt::format_context> store;
auto c = custom_type(); auto c = custom_type();
store.push_back(c); store.push_back(c);
++c.i; ++c.i;
@ -77,7 +79,7 @@ template <> struct formatter<to_stringable> {
FMT_END_NAMESPACE FMT_END_NAMESPACE
TEST(args_test, to_string_and_formatter) { TEST(args_test, to_string_and_formatter) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); fmt::dynamic_format_arg_store<fmt::format_context> store;
auto s = to_stringable(); auto s = to_stringable();
store.push_back(s); store.push_back(s);
store.push_back(std::cref(s)); store.push_back(std::cref(s));
@ -85,13 +87,13 @@ TEST(args_test, to_string_and_formatter) {
} }
TEST(args_test, named_int) { TEST(args_test, named_int) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(fmt::arg("a1", 42)); store.push_back(fmt::arg("a1", 42));
EXPECT_EQ("42", fmt::vformat("{a1}", store)); EXPECT_EQ("42", fmt::vformat("{a1}", store));
} }
TEST(args_test, named_strings) { TEST(args_test, named_strings) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); fmt::dynamic_format_arg_store<fmt::format_context> store;
char str[] = "1234567890"; char str[] = "1234567890";
store.push_back(fmt::arg("a1", str)); store.push_back(fmt::arg("a1", str));
store.push_back(fmt::arg("a2", std::cref(str))); store.push_back(fmt::arg("a2", std::cref(str)));
@ -100,7 +102,7 @@ TEST(args_test, named_strings) {
} }
TEST(args_test, named_arg_by_ref) { TEST(args_test, named_arg_by_ref) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); fmt::dynamic_format_arg_store<fmt::format_context> store;
char band[] = "Rolling Stones"; char band[] = "Rolling Stones";
store.push_back(fmt::arg("band", std::cref(band))); store.push_back(fmt::arg("band", std::cref(band)));
band[9] = 'c'; // Changing band affects the output. band[9] = 'c'; // Changing band affects the output.
@ -108,7 +110,7 @@ TEST(args_test, named_arg_by_ref) {
} }
TEST(args_test, named_custom_format) { TEST(args_test, named_custom_format) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); fmt::dynamic_format_arg_store<fmt::format_context> store;
auto c = custom_type(); auto c = custom_type();
store.push_back(fmt::arg("c1", c)); store.push_back(fmt::arg("c1", c));
++c.i; ++c.i;
@ -121,7 +123,7 @@ TEST(args_test, named_custom_format) {
} }
TEST(args_test, clear) { TEST(args_test, clear) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42); store.push_back(42);
auto result = fmt::vformat("{}", store); auto result = fmt::vformat("{}", store);
@ -138,7 +140,7 @@ TEST(args_test, clear) {
} }
TEST(args_test, reserve) { TEST(args_test, reserve) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); fmt::dynamic_format_arg_store<fmt::format_context> store;
store.reserve(2, 1); store.reserve(2, 1);
store.push_back(1.5f); store.push_back(1.5f);
store.push_back(fmt::arg("a1", 42)); store.push_back(fmt::arg("a1", 42));
@ -163,7 +165,7 @@ template <> struct formatter<copy_throwable> {
FMT_END_NAMESPACE FMT_END_NAMESPACE
TEST(args_test, throw_on_copy) { TEST(args_test, throw_on_copy) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(std::string("foo")); store.push_back(std::string("foo"));
try { try {
store.push_back(copy_throwable()); store.push_back(copy_throwable());
@ -172,15 +174,13 @@ TEST(args_test, throw_on_copy) {
EXPECT_EQ(fmt::vformat("{}", store), "foo"); EXPECT_EQ(fmt::vformat("{}", store), "foo");
} }
TEST(args_test, copy_constructor) { TEST(args_test, move_constructor) {
auto store = fmt::dynamic_format_arg_store<fmt::format_context>(); using store_type = fmt::dynamic_format_arg_store<fmt::format_context>;
store.push_back(fmt::arg("test1", "value1")); auto store = std::unique_ptr<store_type>(new store_type());
store.push_back(fmt::arg("test2", "value2")); store->push_back(42);
store.push_back(fmt::arg("test3", "value3")); store->push_back(std::string("foo"));
store->push_back(fmt::arg("a1", "foo"));
auto store2 = store; auto moved_store = std::move(*store);
store2.push_back(fmt::arg("test4", "value4")); store.reset();
EXPECT_EQ(fmt::vformat("{} {} {a1}", moved_store), "42 foo foo");
auto result = fmt::vformat("{test1} {test2} {test3} {test4}", store2);
EXPECT_EQ(result, "value1 value2 value3 value4");
} }

View File

@ -7,6 +7,9 @@
#include "fmt/chrono.h" #include "fmt/chrono.h"
#include <ctime>
#include <vector>
#include "gtest-extra.h" // EXPECT_THROW_MSG #include "gtest-extra.h" // EXPECT_THROW_MSG
#include "util.h" // get_locale #include "util.h" // get_locale
@ -38,6 +41,36 @@ auto make_second(int s) -> std::tm {
return time; return time;
} }
std::string system_strftime(const std::string& format, const std::tm* timeptr,
std::locale* locptr = nullptr) {
auto loc = locptr ? *locptr : std::locale::classic();
auto& facet = std::use_facet<std::time_put<char>>(loc);
std::ostringstream os;
os.imbue(loc);
facet.put(os, os, ' ', timeptr, format.c_str(),
format.c_str() + format.size());
#ifdef _WIN32
// Workaround a bug in older versions of Universal CRT.
auto str = os.str();
if (str == "-0000") str = "+0000";
return str;
#else
return os.str();
#endif
}
FMT_CONSTEXPR std::tm make_tm(int year, int mon, int mday, int hour, int min,
int sec) {
auto tm = std::tm();
tm.tm_sec = sec;
tm.tm_min = min;
tm.tm_hour = hour;
tm.tm_mday = mday;
tm.tm_mon = mon - 1;
tm.tm_year = year - 1900;
return tm;
}
TEST(chrono_test, format_tm) { TEST(chrono_test, format_tm) {
auto tm = std::tm(); auto tm = std::tm();
tm.tm_year = 116; tm.tm_year = 116;
@ -48,14 +81,132 @@ TEST(chrono_test, format_tm) {
tm.tm_sec = 33; tm.tm_sec = 33;
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
"The date is 2016-04-25 11:22:33."); "The date is 2016-04-25 11:22:33.");
EXPECT_EQ(fmt::format("{:%Y}", tm), "2016");
EXPECT_EQ(fmt::format("{:%C}", tm), "20");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
EXPECT_EQ(fmt::format("{:%e}", tm), "25");
EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/16");
EXPECT_EQ(fmt::format("{:%F}", tm), "2016-04-25");
EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33");
// Short year
tm.tm_year = 999 - 1900;
tm.tm_mon = 0; // for %G
tm.tm_mday = 2; // for %G
tm.tm_wday = 3; // for %G
tm.tm_yday = 1; // for %G
EXPECT_EQ(fmt::format("{:%Y}", tm), "0999");
EXPECT_EQ(fmt::format("{:%C%y}", tm), "0999");
EXPECT_EQ(fmt::format("{:%G}", tm), "0999");
tm.tm_year = 27 - 1900;
EXPECT_EQ(fmt::format("{:%Y}", tm), "0027");
EXPECT_EQ(fmt::format("{:%C%y}", tm), "0027");
// Overflow year
tm.tm_year = 2147483647;
EXPECT_EQ(fmt::format("{:%Y}", tm), "2147485547");
tm.tm_year = -2147483648;
EXPECT_EQ(fmt::format("{:%Y}", tm), "-2147481748");
// for week on the year
// https://www.cl.cam.ac.uk/~mgk25/iso-time.html
std::vector<std::tm> tm_list = {
make_tm(1975, 12, 29, 12, 14, 16), // W01
make_tm(1977, 1, 2, 12, 14, 16), // W53
make_tm(1999, 12, 27, 12, 14, 16), // W52
make_tm(1999, 12, 31, 12, 14, 16), // W52
make_tm(2000, 1, 1, 12, 14, 16), // W52
make_tm(2000, 1, 2, 12, 14, 16), // W52
make_tm(2000, 1, 3, 12, 14, 16) // W1
};
const std::string iso_week_spec = "%Y-%m-%d: %G %g %V";
for (auto ctm : tm_list) {
// Calculate tm_yday, tm_wday, etc.
std::time_t t = std::mktime(&ctm);
tm = *std::localtime(&t);
auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec);
EXPECT_EQ(system_strftime(iso_week_spec, &tm),
fmt::format(fmt::runtime(fmt_spec), tm));
}
// Every day from 1970-01-01
std::time_t time_now = std::time(nullptr);
for (std::time_t t = 6 * 3600; t < time_now; t += 86400) {
tm = *std::localtime(&t);
auto fmt_spec = fmt::format("{{:{}}}", iso_week_spec);
EXPECT_EQ(system_strftime(iso_week_spec, &tm),
fmt::format(fmt::runtime(fmt_spec), tm));
}
} }
// MSVC:
// minkernel\crts\ucrt\src\appcrt\time\wcsftime.cpp(971) : Assertion failed:
// timeptr->tm_year >= -1900 && timeptr->tm_year <= 8099
#ifndef _WIN32
TEST(chrono_test, format_tm_future) {
auto tm = std::tm();
tm.tm_year = 10445; // 10000+ years
tm.tm_mon = 3;
tm.tm_mday = 25;
tm.tm_hour = 11;
tm.tm_min = 22;
tm.tm_sec = 33;
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
"The date is 12345-04-25 11:22:33.");
EXPECT_EQ(fmt::format("{:%Y}", tm), "12345");
EXPECT_EQ(fmt::format("{:%C}", tm), "123");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/45");
EXPECT_EQ(fmt::format("{:%F}", tm), "12345-04-25");
EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33");
}
TEST(chrono_test, format_tm_past) {
auto tm = std::tm();
tm.tm_year = -2001;
tm.tm_mon = 3;
tm.tm_mday = 25;
tm.tm_hour = 11;
tm.tm_min = 22;
tm.tm_sec = 33;
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
"The date is -101-04-25 11:22:33.");
EXPECT_EQ(fmt::format("{:%Y}", tm), "-101");
// macOS %C - "-1"
// Linux %C - "-2"
// fmt %C - "-1"
EXPECT_EQ(fmt::format("{:%C}", tm), "-1");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
// macOS %D - "04/25/01" (%y)
// Linux %D - "04/25/99" (%y)
// fmt %D - "04/25/01" (%y)
EXPECT_EQ(fmt::format("{:%D}", tm), "04/25/01");
EXPECT_EQ(fmt::format("{:%F}", tm), "-101-04-25");
EXPECT_EQ(fmt::format("{:%T}", tm), "11:22:33");
tm.tm_year = -1901; // -1
EXPECT_EQ(fmt::format("{:%Y}", tm), "-001");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
tm.tm_year = -1911; // -11
EXPECT_EQ(fmt::format("{:%Y}", tm), "-011");
EXPECT_EQ(fmt::format("{:%C%y}", tm), fmt::format("{:%Y}", tm));
}
#endif
TEST(chrono_test, grow_buffer) { TEST(chrono_test, grow_buffer) {
auto s = std::string("{:"); auto s = std::string("{:");
for (int i = 0; i < 30; ++i) s += "%c"; for (int i = 0; i < 30; ++i) s += "%c";
s += "}\n"; s += "}\n";
auto t = std::time(nullptr); auto t = std::time(nullptr);
fmt::format(fmt::runtime(s), *std::localtime(&t)); (void)fmt::format(fmt::runtime(s), *std::localtime(&t));
} }
TEST(chrono_test, format_to_empty_container) { TEST(chrono_test, format_to_empty_container) {
@ -88,22 +239,45 @@ TEST(chrono_test, gmtime) {
EXPECT_TRUE(equal(tm, fmt::gmtime(t))); EXPECT_TRUE(equal(tm, fmt::gmtime(t)));
} }
template <typename TimePoint> auto strftime(TimePoint tp) -> std::string { template <typename TimePoint> auto strftime_full(TimePoint tp) -> std::string {
auto t = std::chrono::system_clock::to_time_t(tp); auto t = std::chrono::system_clock::to_time_t(tp);
auto tm = *std::localtime(&t); auto tm = *std::localtime(&t);
char output[256] = {}; return system_strftime("%Y-%m-%d %H:%M:%S", &tm);
std::strftime(output, sizeof(output), "%Y-%m-%d %H:%M:%S", &tm);
return output;
} }
TEST(chrono_test, time_point) { TEST(chrono_test, time_point) {
auto t1 = std::chrono::system_clock::now(); auto t1 = std::chrono::system_clock::now();
EXPECT_EQ(strftime(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1)); EXPECT_EQ(strftime_full(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1));
EXPECT_EQ(strftime(t1), fmt::format("{}", t1)); EXPECT_EQ(strftime_full(t1), fmt::format("{}", t1));
using time_point = using time_point =
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>; std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
auto t2 = time_point(std::chrono::seconds(42)); auto t2 = time_point(std::chrono::seconds(42));
EXPECT_EQ(strftime(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2)); EXPECT_EQ(strftime_full(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2));
std::vector<std::string> spec_list = {
"%%", "%n", "%t", "%Y", "%EY", "%y", "%Oy", "%Ey", "%C",
"%EC", "%G", "%g", "%b", "%h", "%B", "%m", "%Om", "%U",
"%OU", "%W", "%OW", "%V", "%OV", "%j", "%d", "%Od", "%e",
"%Oe", "%a", "%A", "%w", "%Ow", "%u", "%Ou", "%H", "%OH",
"%I", "%OI", "%M", "%OM", "%S", "%OS", "%x", "%Ex", "%X",
"%EX", "%D", "%F", "%R", "%T", "%p", "%z", "%Z"};
spec_list.push_back("%Y-%m-%d %H:%M:%S");
#ifndef _WIN32
// Disabled on Windows because these formats are not consistent among
// platforms.
spec_list.insert(spec_list.end(), {"%c", "%Ec", "%r"});
#endif
for (const auto& spec : spec_list) {
auto t = std::chrono::system_clock::to_time_t(t1);
auto tm = *std::localtime(&t);
auto sys_output = system_strftime(spec, &tm);
auto fmt_spec = fmt::format("{{:{}}}", spec);
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), t1));
EXPECT_EQ(sys_output, fmt::format(fmt::runtime(fmt_spec), tm));
}
} }
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
@ -197,41 +371,41 @@ TEST(chrono_test, format_specs) {
TEST(chrono_test, invalid_specs) { TEST(chrono_test, invalid_specs) {
auto sec = std::chrono::seconds(0); auto sec = std::chrono::seconds(0);
EXPECT_THROW_MSG(fmt::format(runtime("{:%a}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%a}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%A}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%A}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%c}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%c}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%x}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%x}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Ex}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Ex}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%X}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%X}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%EX}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%EX}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%D}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%D}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%F}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%F}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Ec}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Ec}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%w}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%w}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%u}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%u}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%b}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%b}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%B}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%B}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%z}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%z}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Z}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Z}"), sec), fmt::format_error,
"no date"); "no date");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Eq}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Eq}"), sec), fmt::format_error,
"invalid format"); "invalid format");
EXPECT_THROW_MSG(fmt::format(runtime("{:%Oq}"), sec), fmt::format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:%Oq}"), sec), fmt::format_error,
"invalid format"); "invalid format");
} }
@ -279,20 +453,33 @@ TEST(chrono_test, format_default_fp) {
} }
TEST(chrono_test, format_precision) { TEST(chrono_test, format_precision) {
EXPECT_THROW_MSG(fmt::format(runtime("{:.2}"), std::chrono::seconds(42)), EXPECT_THROW_MSG(
fmt::format_error, (void)fmt::format(runtime("{:.2}"), std::chrono::seconds(42)),
"precision not allowed for this argument type"); fmt::format_error, "precision not allowed for this argument type");
EXPECT_EQ("1ms", fmt::format("{:.0}", dms(1.234)));
EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234))); EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234)));
EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2)); EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2));
EXPECT_EQ("13ms", fmt::format("{:.0}", dms(12.56)));
EXPECT_EQ("12.6ms", fmt::format("{:.1}", dms(12.56)));
EXPECT_EQ("12.56ms", fmt::format("{:.2}", dms(12.56)));
} }
TEST(chrono_test, format_full_specs) { TEST(chrono_test, format_full_specs) {
EXPECT_EQ("1ms ", fmt::format("{:6.0}", dms(1.234)));
EXPECT_EQ("1.2ms ", fmt::format("{:6.1}", dms(1.234))); EXPECT_EQ("1.2ms ", fmt::format("{:6.1}", dms(1.234)));
EXPECT_EQ(" 1.23ms", fmt::format("{:>8.{}}", dms(1.234), 2)); EXPECT_EQ(" 1.23ms", fmt::format("{:>8.{}}", dms(1.234), 2));
EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1)); EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1));
EXPECT_EQ(" 1.23ms ", fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8)); EXPECT_EQ(" 1.23ms ", fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8));
EXPECT_EQ("=1.234ms=", fmt::format("{:=^{}.{}}", dms(1.234), 9, 3)); EXPECT_EQ("=1.234ms=", fmt::format("{:=^{}.{}}", dms(1.234), 9, 3));
EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234))); EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234)));
EXPECT_EQ("13ms ", fmt::format("{:6.0}", dms(12.56)));
EXPECT_EQ(" 13ms", fmt::format("{:>8.{}}", dms(12.56), 0));
EXPECT_EQ(" 13ms ", fmt::format("{:^{}.{}}", dms(12.56), 6, 0));
EXPECT_EQ(" 13ms ", fmt::format("{0:^{2}.{1}}", dms(12.56), 0, 8));
EXPECT_EQ("==13ms===", fmt::format("{:=^{}.{}}", dms(12.56), 9, 0));
EXPECT_EQ("***13ms***", fmt::format("{:*^10.0}", dms(12.56)));
} }
TEST(chrono_test, format_simple_q) { TEST(chrono_test, format_simple_q) {
@ -306,29 +493,37 @@ TEST(chrono_test, format_simple_q) {
} }
TEST(chrono_test, format_precision_q) { TEST(chrono_test, format_precision_q) {
EXPECT_THROW_MSG(fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)), EXPECT_THROW_MSG(
fmt::format_error, (void)fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)),
"precision not allowed for this argument type"); fmt::format_error, "precision not allowed for this argument type");
EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234))); EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234)));
EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2)); EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2));
} }
TEST(chrono_test, format_full_specs_q) { TEST(chrono_test, format_full_specs_q) {
EXPECT_EQ("1 ms ", fmt::format("{:7.0%Q %q}", dms(1.234)));
EXPECT_EQ("1.2 ms ", fmt::format("{:7.1%Q %q}", dms(1.234))); EXPECT_EQ("1.2 ms ", fmt::format("{:7.1%Q %q}", dms(1.234)));
EXPECT_EQ(" 1.23 ms", fmt::format("{:>8.{}%Q %q}", dms(1.234), 2)); EXPECT_EQ(" 1.23 ms", fmt::format("{:>8.{}%Q %q}", dms(1.234), 2));
EXPECT_EQ(" 1.2 ms ", fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1)); EXPECT_EQ(" 1.2 ms ", fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1));
EXPECT_EQ(" 1.23 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9)); EXPECT_EQ(" 1.23 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9));
EXPECT_EQ("=1.234 ms=", fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3)); EXPECT_EQ("=1.234 ms=", fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3));
EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234))); EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234)));
EXPECT_EQ("13 ms ", fmt::format("{:7.0%Q %q}", dms(12.56)));
EXPECT_EQ(" 13 ms", fmt::format("{:>8.{}%Q %q}", dms(12.56), 0));
EXPECT_EQ(" 13 ms ", fmt::format("{:^{}.{}%Q %q}", dms(12.56), 8, 0));
EXPECT_EQ(" 13 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(12.56), 0, 9));
EXPECT_EQ("==13 ms==", fmt::format("{:=^{}.{}%Q %q}", dms(12.56), 9, 0));
EXPECT_EQ("***13 ms***", fmt::format("{:*^11.0%Q %q}", dms(12.56)));
} }
TEST(chrono_test, invalid_width_id) { TEST(chrono_test, invalid_width_id) {
EXPECT_THROW(fmt::format(runtime("{:{o}"), std::chrono::seconds(0)), EXPECT_THROW((void)fmt::format(runtime("{:{o}"), std::chrono::seconds(0)),
fmt::format_error); fmt::format_error);
} }
TEST(chrono_test, invalid_colons) { TEST(chrono_test, invalid_colons) {
EXPECT_THROW(fmt::format(runtime("{0}=:{0::"), std::chrono::seconds(0)), EXPECT_THROW((void)fmt::format(runtime("{0}=:{0::"), std::chrono::seconds(0)),
fmt::format_error); fmt::format_error);
} }
@ -348,15 +543,12 @@ TEST(chrono_test, negative_durations) {
} }
TEST(chrono_test, special_durations) { TEST(chrono_test, special_durations) {
EXPECT_EQ( auto value = fmt::format("{:%S}", std::chrono::duration<double>(1e20));
"40.", EXPECT_EQ(value, "40");
fmt::format("{:%S}", std::chrono::duration<double>(1e20)).substr(0, 3));
auto nan = std::numeric_limits<double>::quiet_NaN(); auto nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ( EXPECT_EQ(
"nan nan nan nan nan:nan nan", "nan nan nan nan nan:nan nan",
fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan))); fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan)));
fmt::format("{:%S}",
std::chrono::duration<float, std::atto>(1.79400457e+31f));
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::exa>(1)), EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::exa>(1)),
"1Es"); "1Es");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::atto>(1)), EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::atto>(1)),
@ -365,6 +557,9 @@ TEST(chrono_test, special_durations) {
"03:33"); "03:33");
EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}), EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}),
"03:33:20"); "03:33:20");
EXPECT_EQ("44.000000000000",
fmt::format("{:%S}", std::chrono::duration<float, std::pico>(
1.54213895E+26)));
} }
TEST(chrono_test, unsigned_duration) { TEST(chrono_test, unsigned_duration) {
@ -375,11 +570,63 @@ TEST(chrono_test, weekday) {
auto loc = get_locale("ru_RU.UTF-8"); auto loc = get_locale("ru_RU.UTF-8");
std::locale::global(loc); std::locale::global(loc);
auto mon = fmt::weekday(1); auto mon = fmt::weekday(1);
auto tm = std::tm();
tm.tm_wday = static_cast<int>(mon.c_encoding());
EXPECT_EQ(fmt::format("{}", mon), "Mon"); EXPECT_EQ(fmt::format("{}", mon), "Mon");
EXPECT_EQ(fmt::format("{:%a}", tm), "Mon");
if (loc != std::locale::classic()) { if (loc != std::locale::classic()) {
EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}), EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}),
Contains(fmt::format(loc, "{:L}", mon))); Contains(fmt::format(loc, "{:L}", mon)));
EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}),
Contains(fmt::format(loc, "{:%a}", tm)));
} }
} }
TEST(chrono_test, cpp20_duration_subsecond_support) {
using attoseconds = std::chrono::duration<long long, std::atto>;
// Check that 18 digits of subsecond precision are supported.
EXPECT_EQ(fmt::format("{:%S}", attoseconds{999999999999999999}),
"00.999999999999999999");
EXPECT_EQ(fmt::format("{:%S}", attoseconds{673231113420148734}),
"00.673231113420148734");
EXPECT_EQ(fmt::format("{:%S}", attoseconds{-673231113420148734}),
"-00.673231113420148734");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{13420148734}),
"13.420148734");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::nanoseconds{-13420148734}),
"-13.420148734");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds{1234}), "01.234");
{
// Check that {:%H:%M:%S} is equivalent to {:%T}.
auto dur = std::chrono::milliseconds{3601234};
auto formatted_dur = fmt::format("{:%T}", dur);
EXPECT_EQ(formatted_dur, "01:00:01.234");
EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur);
}
using nanoseconds_dbl = std::chrono::duration<double, std::nano>;
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{-123456789}), "-00.123456789");
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{9123456789}), "09.123456789");
// Verify that only the seconds part is extracted and printed.
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123456789}), "39.123456789");
EXPECT_EQ(fmt::format("{:%S}", nanoseconds_dbl{99123000000}), "39.123000000");
{
// Now the hour is printed, and we also test if negative doubles work.
auto dur = nanoseconds_dbl{-99123456789};
auto formatted_dur = fmt::format("{:%T}", dur);
EXPECT_EQ(formatted_dur, "-00:01:39.123456789");
EXPECT_EQ(fmt::format("{:%H:%M:%S}", dur), formatted_dur);
}
// Check that durations with precision greater than std::chrono::seconds have
// fixed precision, and print zeros even if there is no fractional part.
EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}),
"07.000000");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 3>>(1)),
"00.333333");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 7>>(1)),
"00.142857");
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR #endif // FMT_STATIC_THOUSANDS_SEPARATOR

View File

@ -50,6 +50,12 @@ TEST(color_test, format) {
"\x1b[105mtbmagenta\x1b[0m"); "\x1b[105mtbmagenta\x1b[0m");
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"), EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
"\x1b[31mfoo\x1b[0m"); "\x1b[31mfoo\x1b[0m");
EXPECT_EQ(fmt::format("{}{}", fmt::styled("red", fg(fmt::color::red)),
fmt::styled("bold", fmt::emphasis::bold)),
"\x1b[38;2;255;000;000mred\x1b[0m\x1b[1mbold\x1b[0m");
EXPECT_EQ(fmt::format("{}", fmt::styled("bar", fg(fmt::color::blue) |
fmt::emphasis::underline)),
"\x1b[4m\x1b[38;2;000;000;255mbar\x1b[0m");
} }
TEST(color_test, format_to) { TEST(color_test, format_to) {

View File

@ -1,71 +1,158 @@
# Test if compile errors are produced where necessary. # Test if compile errors are produced where necessary.
cmake_minimum_required(VERSION 3.1...3.18) cmake_minimum_required(VERSION 3.1...3.18)
project(compile-error-test CXX)
include(CheckCXXSourceCompiles) set(fmt_headers "
include(CheckCXXCompilerFlag) #include <fmt/format.h>
#include <fmt/xchar.h>
")
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../../include) set(error_test_names "")
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG} ${PEDANTIC_COMPILE_FLAGS}) set(non_error_test_content "")
function (generate_source result fragment) # For error tests (we expect them to produce compilation error):
set(${result} " # * adds a name of test into `error_test_names` list
#define FMT_HEADER_ONLY 1 # * generates a single source file (with the same name) for each test
#include \"fmt/format.h\" # For non-error tests (we expect them to compile successfully):
int main() { # * adds a code segment as separate function to `non_error_test_content`
${fragment} function (expect_compile name code_fragment)
} cmake_parse_arguments(EXPECT_COMPILE "ERROR" "" "" ${ARGN})
" PARENT_SCOPE) string(MAKE_C_IDENTIFIER "${name}" test_name)
if (EXPECT_COMPILE_ERROR)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/${test_name}.cc" "
${fmt_headers}
void ${test_name}() {
${code_fragment}
}
")
set(error_test_names_copy "${error_test_names}")
list(APPEND error_test_names_copy "${test_name}")
set(error_test_names "${error_test_names_copy}" PARENT_SCOPE)
else()
set(non_error_test_content "
${non_error_test_content}
void ${test_name}() {
${code_fragment}
}" PARENT_SCOPE)
endif()
endfunction () endfunction ()
function (expect_compile code) # Generates a source file for non-error test with `non_error_test_content` and
generate_source(source "${code}") # CMake project file with all error and single non-error test targets.
check_cxx_source_compiles("${source}" compiles) function (run_tests)
if (NOT compiles) set(cmake_targets "")
set(error_msg "Compile error for: ${code}") foreach(test_name IN LISTS error_test_names)
endif () set(cmake_targets "
# Unset the CMake cache variable compiles. Otherwise the compile test will ${cmake_targets}
# just use cached information next time it runs. add_library(test-${test_name} ${test_name}.cc)
unset(compiles CACHE) target_link_libraries(test-${test_name} PRIVATE fmt::fmt)
if (error_msg) ")
message(FATAL_ERROR ${error_msg}) endforeach()
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/non_error_test.cc" "
${fmt_headers}
${non_error_test_content}
")
set(cmake_targets "
${cmake_targets}
add_library(non-error-test non_error_test.cc)
target_link_libraries(non-error-test PRIVATE fmt::fmt)
")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test/CMakeLists.txt" "
cmake_minimum_required(VERSION 3.1...3.18)
project(tests CXX)
add_subdirectory(${FMT_DIR} fmt)
${cmake_targets}
")
set(build_directory "${CMAKE_CURRENT_BINARY_DIR}/test/build")
file(MAKE_DIRECTORY "${build_directory}")
execute_process(
COMMAND
"${CMAKE_COMMAND}"
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
"-DCMAKE_GENERATOR=${CMAKE_GENERATOR}"
"-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}"
"-DFMT_DIR=${FMT_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}/test"
WORKING_DIRECTORY "${build_directory}"
RESULT_VARIABLE result_var
OUTPUT_VARIABLE output_var
ERROR_VARIABLE output_var)
if (NOT result_var EQUAL 0)
message(FATAL_ERROR "Unable to configure:\n${output_var}")
endif()
foreach(test_name IN LISTS error_test_names)
execute_process(
COMMAND
"${CMAKE_COMMAND}" --build "${build_directory}" --target "test-${test_name}"
WORKING_DIRECTORY "${build_directory}"
RESULT_VARIABLE result_var
OUTPUT_VARIABLE output_var
ERROR_QUIET)
if (result_var EQUAL 0)
message(SEND_ERROR "No compile error for \"${test_name}\":\n${output_var}")
endif ()
endforeach()
execute_process(
COMMAND
"${CMAKE_COMMAND}" --build "${build_directory}" --target "non-error-test"
WORKING_DIRECTORY "${build_directory}"
RESULT_VARIABLE result_var
OUTPUT_VARIABLE output_var
ERROR_VARIABLE output_var)
if (NOT result_var EQUAL 0)
message(SEND_ERROR "Compile error for combined non-error test:\n${output_var}")
endif () endif ()
endfunction () endfunction ()
function (expect_compile_error code)
generate_source(source "${code}")
check_cxx_source_compiles("${source}" compiles)
if (compiles)
set(error_msg "No compile error for: ${code}")
endif ()
# Unset the CMake cache variable compiles. Otherwise the compile test will
# just use cached information next time it runs.
unset(compiles CACHE)
if (error_msg)
message(FATAL_ERROR ${error_msg})
endif ()
endfunction ()
# check if the source file skeleton compiles # check if the source file skeleton compiles
expect_compile("") expect_compile(check "")
expect_compile(check-error "compilation_error" ERROR)
# Formatting a wide character with a narrow format string is forbidden. # Formatting a wide character with a narrow format string is forbidden.
expect_compile_error("fmt::format(\"{}\", L'a');") expect_compile(wide-character-narrow-format-string "fmt::format(L\"{}\", L'a');")
expect_compile(wide-character-narrow-format-string-error "fmt::format(\"{}\", L'a');" ERROR)
# Formatting a wide string with a narrow format string is forbidden. # Formatting a wide string with a narrow format string is forbidden.
expect_compile_error("fmt::format(\"{}\", L\"foo\");") expect_compile(wide-string-narrow-format-string "fmt::format(L\"{}\", L\"foo\");")
expect_compile(wide-string-narrow-format-string-error "fmt::format(\"{}\", L\"foo\");" ERROR)
# Formatting a narrow string with a wide format string is forbidden because # Formatting a narrow string with a wide format string is forbidden because
# mixing UTF-8 with UTF-16/32 can result in an invalid output. # mixing UTF-8 with UTF-16/32 can result in an invalid output.
expect_compile_error("fmt::format(L\"{}\", \"foo\");") expect_compile(narrow-string-wide-format-string "fmt::format(L\"{}\", L\"foo\");")
expect_compile(narrow-string-wide-format-string-error "fmt::format(L\"{}\", \"foo\");" ERROR)
# Formatting a wide string with a narrow format string is forbidden. expect_compile(cast-to-string "
expect_compile_error(" struct S {
operator std::string() const { return std::string(); }
};
fmt::format(\"{}\", std::string(S()));
")
expect_compile(cast-to-string-error "
struct S { struct S {
operator std::string() const { return std::string(); } operator std::string() const { return std::string(); }
}; };
fmt::format(\"{}\", S()); fmt::format(\"{}\", S());
" ERROR)
# Formatting a function
expect_compile(format-function "
void (*f)();
fmt::format(\"{}\", fmt::ptr(f));
") ")
expect_compile(format-function-error "
void (*f)();
fmt::format(\"{}\", f);
" ERROR)
# Make sure that compiler features detected in the header # Make sure that compiler features detected in the header
# match the features detected in CMake. # match the features detected in CMake.
@ -74,6 +161,43 @@ if (SUPPORTS_USER_DEFINED_LITERALS)
else () else ()
set(supports_udl 0) set(supports_udl 0)
endif () endif ()
expect_compile("#if FMT_USE_USER_DEFINED_LITERALS != ${supports_udl} expect_compile(udl-check "
# error #if FMT_USE_USER_DEFINED_LITERALS != ${supports_udl}
#endif") # error
#endif
")
if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
# Compile-time argument type check
expect_compile(format-string-number-spec "
#ifdef FMT_HAS_CONSTEVAL
fmt::format(\"{:d}\", 42);
#endif
")
expect_compile(format-string-number-spec-error "
#ifdef FMT_HAS_CONSTEVAL
fmt::format(\"{:d}\", \"I am not a number\");
#else
#error
#endif
" ERROR)
# Compile-time argument name check
expect_compile(format-string-name "
#if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
using namespace fmt::literals;
fmt::print(\"{foo}\", \"foo\"_a=42);
#endif
")
expect_compile(format-string-name-error "
#if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
using namespace fmt::literals;
fmt::print(\"{foo}\", \"bar\"_a=42);
#else
#error
#endif
" ERROR)
endif ()
# Run all tests
run_tests()

62
vendor/Fmt/test/compile-fp-test.cc vendored Normal file
View File

@ -0,0 +1,62 @@
// Formatting library for C++ - formatting library tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/compile.h"
#include "gmock/gmock.h"
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 && \
defined(__cpp_constexpr) && __cpp_constexpr >= 201907 && \
defined(__cpp_constexpr_dynamic_alloc) && \
__cpp_constexpr_dynamic_alloc >= 201907 && __cplusplus >= 202002L
template <size_t max_string_length, typename Char = char> struct test_string {
template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;
}
Char buffer[max_string_length]{};
};
template <size_t max_string_length, typename Char = char, typename... Args>
consteval auto test_format(auto format, const Args&... args) {
test_string<max_string_length, Char> string{};
fmt::format_to(string.buffer, format, args...);
return string;
}
TEST(compile_time_formatting_test, floating_point) {
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{}"), 0.0f));
EXPECT_EQ("392.500000", test_format<11>(FMT_COMPILE("{0:f}"), 392.5f));
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:}"), 0.0));
EXPECT_EQ("0.000000", test_format<9>(FMT_COMPILE("{:f}"), 0.0));
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:g}"), 0.0));
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:}"), 392.65));
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:g}"), 392.65));
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:G}"), 392.65));
EXPECT_EQ("4.9014e+06", test_format<11>(FMT_COMPILE("{:g}"), 4.9014e6));
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:f}"), -392.65));
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:F}"), -392.65));
EXPECT_EQ("3.926500e+02", test_format<13>(FMT_COMPILE("{0:e}"), 392.65));
EXPECT_EQ("3.926500E+02", test_format<13>(FMT_COMPILE("{0:E}"), 392.65));
EXPECT_EQ("+0000392.6", test_format<11>(FMT_COMPILE("{0:+010.4g}"), 392.65));
EXPECT_EQ("9223372036854775808.000000",
test_format<27>(FMT_COMPILE("{:f}"), 9223372036854775807.0));
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ("nan", test_format<4>(FMT_COMPILE("{}"), nan));
EXPECT_EQ("+nan", test_format<5>(FMT_COMPILE("{:+}"), nan));
if (std::signbit(-nan))
EXPECT_EQ("-nan", test_format<5>(FMT_COMPILE("{}"), -nan));
else
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
constexpr double inf = std::numeric_limits<double>::infinity();
EXPECT_EQ("inf", test_format<4>(FMT_COMPILE("{}"), inf));
EXPECT_EQ("+inf", test_format<5>(FMT_COMPILE("{:+}"), inf));
EXPECT_EQ("-inf", test_format<5>(FMT_COMPILE("{}"), -inf));
}
#endif

View File

@ -59,7 +59,24 @@ TEST(compile_test, compile_fallback) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42)); EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), 42));
} }
#ifdef __cpp_if_constexpr struct type_with_get {
template <int> friend void get(type_with_get);
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<type_with_get> : formatter<int> {
template <typename FormatContext>
auto format(type_with_get, FormatContext& ctx) -> decltype(ctx.out()) {
return formatter<int>::format(42, ctx);
}
};
FMT_END_NAMESPACE
TEST(compile_test, compile_type_with_get) {
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), type_with_get()));
}
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
struct test_formattable {}; struct test_formattable {};
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -184,6 +201,11 @@ TEST(compile_test, named) {
# endif # endif
} }
TEST(compile_test, join) {
unsigned char data[] = {0x1, 0x2, 0xaf};
EXPECT_EQ("0102af", fmt::format(FMT_COMPILE("{:02x}"), fmt::join(data, "")));
}
TEST(compile_test, format_to) { TEST(compile_test, format_to) {
char buf[8]; char buf[8];
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42); auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);

View File

@ -408,15 +408,7 @@ TYPED_TEST(numeric_arg_test, make_and_visit) {
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max()); CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max());
} }
namespace fmt { TEST(arg_test, char_arg) { CHECK_ARG(char, 'a', 'a'); }
template <> struct is_char<wchar_t> : std::true_type {};
} // namespace fmt
TEST(arg_test, char_arg) {
CHECK_ARG(char, 'a', 'a');
CHECK_ARG(wchar_t, L'a', 'a');
CHECK_ARG(wchar_t, L'a', L'a');
}
TEST(arg_test, string_arg) { TEST(arg_test, string_arg) {
char str_data[] = "test"; char str_data[] = "test";
@ -532,7 +524,7 @@ struct test_format_specs_handler {
fmt::detail::arg_ref<char> width_ref; fmt::detail::arg_ref<char> width_ref;
int precision = 0; int precision = 0;
fmt::detail::arg_ref<char> precision_ref; fmt::detail::arg_ref<char> precision_ref;
char type = 0; fmt::presentation_type type = fmt::presentation_type::none;
// Workaround for MSVC2017 bug that results in "expression did not evaluate // Workaround for MSVC2017 bug that results in "expression did not evaluate
// to a constant" with compiler-generated copy ctor. // to a constant" with compiler-generated copy ctor.
@ -558,14 +550,14 @@ struct test_format_specs_handler {
constexpr void on_dynamic_precision(string_view) {} constexpr void on_dynamic_precision(string_view) {}
constexpr void end_precision() {} constexpr void end_precision() {}
constexpr void on_type(char t) { type = t; } constexpr void on_type(fmt::presentation_type t) { type = t; }
constexpr void on_error(const char*) { res = error; } constexpr void on_error(const char*) { res = error; }
}; };
template <size_t N> template <size_t N>
constexpr test_format_specs_handler parse_test_specs(const char (&s)[N]) { constexpr test_format_specs_handler parse_test_specs(const char (&s)[N]) {
auto h = test_format_specs_handler(); auto h = test_format_specs_handler();
fmt::detail::parse_format_specs(s, s + N, h); fmt::detail::parse_format_specs(s, s + N - 1, h);
return h; return h;
} }
@ -583,7 +575,7 @@ TEST(core_test, constexpr_parse_format_specs) {
static_assert(parse_test_specs("{42}").width_ref.val.index == 42, ""); static_assert(parse_test_specs("{42}").width_ref.val.index == 42, "");
static_assert(parse_test_specs(".42").precision == 42, ""); static_assert(parse_test_specs(".42").precision == 42, "");
static_assert(parse_test_specs(".{42}").precision_ref.val.index == 42, ""); static_assert(parse_test_specs(".{42}").precision_ref.val.index == 42, "");
static_assert(parse_test_specs("d").type == 'd', ""); static_assert(parse_test_specs("d").type == fmt::presentation_type::dec, "");
static_assert(parse_test_specs("{<").res == handler::error, ""); static_assert(parse_test_specs("{<").res == handler::error, "");
} }
@ -605,7 +597,7 @@ constexpr fmt::detail::dynamic_format_specs<char> parse_dynamic_specs(
auto specs = fmt::detail::dynamic_format_specs<char>(); auto specs = fmt::detail::dynamic_format_specs<char>();
auto ctx = test_parse_context(); auto ctx = test_parse_context();
auto h = fmt::detail::dynamic_specs_handler<test_parse_context>(specs, ctx); auto h = fmt::detail::dynamic_specs_handler<test_parse_context>(specs, ctx);
parse_format_specs(s, s + N, h); parse_format_specs(s, s + N - 1, h);
return specs; return specs;
} }
@ -623,14 +615,15 @@ TEST(format_test, constexpr_dynamic_specs_handler) {
static_assert(parse_dynamic_specs(".42").precision == 42, ""); static_assert(parse_dynamic_specs(".42").precision == 42, "");
static_assert(parse_dynamic_specs(".{}").precision_ref.val.index == 11, ""); static_assert(parse_dynamic_specs(".{}").precision_ref.val.index == 11, "");
static_assert(parse_dynamic_specs(".{42}").precision_ref.val.index == 42, ""); static_assert(parse_dynamic_specs(".{42}").precision_ref.val.index == 42, "");
static_assert(parse_dynamic_specs("d").type == 'd', ""); static_assert(parse_dynamic_specs("d").type == fmt::presentation_type::dec,
"");
} }
template <size_t N> template <size_t N>
constexpr test_format_specs_handler check_specs(const char (&s)[N]) { constexpr test_format_specs_handler check_specs(const char (&s)[N]) {
fmt::detail::specs_checker<test_format_specs_handler> checker( fmt::detail::specs_checker<test_format_specs_handler> checker(
test_format_specs_handler(), fmt::detail::type::double_type); test_format_specs_handler(), fmt::detail::type::double_type);
parse_format_specs(s, s + N, checker); parse_format_specs(s, s + N - 1, checker);
return checker; return checker;
} }
@ -647,7 +640,7 @@ TEST(format_test, constexpr_specs_checker) {
static_assert(check_specs("{42}").width_ref.val.index == 42, ""); static_assert(check_specs("{42}").width_ref.val.index == 42, "");
static_assert(check_specs(".42").precision == 42, ""); static_assert(check_specs(".42").precision == 42, "");
static_assert(check_specs(".{42}").precision_ref.val.index == 42, ""); static_assert(check_specs(".{42}").precision_ref.val.index == 42, "");
static_assert(check_specs("d").type == 'd', ""); static_assert(check_specs("d").type == fmt::presentation_type::dec, "");
static_assert(check_specs("{<").res == handler::error, ""); static_assert(check_specs("{<").res == handler::error, "");
} }
@ -711,10 +704,110 @@ TEST(core_test, has_formatter) {
""); "");
} }
struct const_formattable {};
struct nonconst_formattable {};
FMT_BEGIN_NAMESPACE
template <> struct formatter<const_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(const const_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
template <> struct formatter<nonconst_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(nonconst_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
FMT_END_NAMESPACE
struct convertible_to_pointer {
operator const int*() const { return nullptr; }
};
struct convertible_to_pointer_formattable {
operator const int*() const { return nullptr; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<convertible_to_pointer_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(convertible_to_pointer_formattable, format_context& ctx) const
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
FMT_END_NAMESPACE
enum class unformattable_scoped_enum {};
namespace test {
enum class formattable_scoped_enum {};
auto format_as(formattable_scoped_enum) -> int { return 42; }
struct convertible_to_enum {
operator formattable_scoped_enum() const { return {}; }
};
} // namespace test
TEST(core_test, is_formattable) { TEST(core_test, is_formattable) {
#if 0
// This should be enabled once corresponding map overloads are gone.
static_assert(fmt::is_formattable<signed char*>::value, "");
static_assert(fmt::is_formattable<unsigned char*>::value, "");
static_assert(fmt::is_formattable<const signed char*>::value, "");
static_assert(fmt::is_formattable<const unsigned char*>::value, "");
#endif
static_assert(!fmt::is_formattable<wchar_t>::value, "");
#ifdef __cpp_char8_t
static_assert(!fmt::is_formattable<char8_t>::value, "");
#endif
static_assert(!fmt::is_formattable<char16_t>::value, "");
static_assert(!fmt::is_formattable<char32_t>::value, "");
static_assert(!fmt::is_formattable<const wchar_t*>::value, "");
static_assert(!fmt::is_formattable<const wchar_t[3]>::value, "");
static_assert(!fmt::is_formattable<fmt::basic_string_view<wchar_t>>::value,
"");
static_assert(fmt::is_formattable<enabled_formatter>::value, ""); static_assert(fmt::is_formattable<enabled_formatter>::value, "");
static_assert(!fmt::is_formattable<disabled_formatter>::value, ""); static_assert(!fmt::is_formattable<disabled_formatter>::value, "");
static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, ""); static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, "");
static_assert(fmt::is_formattable<const_formattable&>::value, "");
static_assert(fmt::is_formattable<const const_formattable&>::value, "");
static_assert(fmt::is_formattable<nonconst_formattable&>::value, "");
#if !FMT_MSC_VER || FMT_MSC_VER >= 1910
static_assert(!fmt::is_formattable<const nonconst_formattable&>::value, "");
#endif
static_assert(!fmt::is_formattable<convertible_to_pointer>::value, "");
const auto f = convertible_to_pointer_formattable();
EXPECT_EQ(fmt::format("{}", f), "test");
static_assert(!fmt::is_formattable<void (*)()>::value, "");
struct s;
static_assert(!fmt::is_formattable<int(s::*)>::value, "");
static_assert(!fmt::is_formattable<int (s::*)()>::value, "");
static_assert(!fmt::is_formattable<unformattable_scoped_enum>::value, "");
static_assert(fmt::is_formattable<test::formattable_scoped_enum>::value, "");
static_assert(!fmt::is_formattable<test::convertible_to_enum>::value, "");
} }
TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); } TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); }
@ -725,6 +818,10 @@ TEST(core_test, format_to) {
EXPECT_EQ(s, "42"); EXPECT_EQ(s, "42");
} }
TEST(core_test, format_as) {
EXPECT_EQ(fmt::format("{}", test::formattable_scoped_enum()), "42");
}
struct convertible_to_int { struct convertible_to_int {
operator int() const { return 42; } operator int() const { return 42; }
}; };
@ -793,7 +890,10 @@ struct explicitly_convertible_to_string_view {
}; };
TEST(core_test, format_explicitly_convertible_to_string_view) { TEST(core_test, format_explicitly_convertible_to_string_view) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view())); // Types explicitly convertible to string_view are not formattable by
// default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_string_view>::value, "");
} }
# ifdef FMT_USE_STRING_VIEW # ifdef FMT_USE_STRING_VIEW
@ -802,8 +902,11 @@ struct explicitly_convertible_to_std_string_view {
}; };
TEST(core_test, format_explicitly_convertible_to_std_string_view) { TEST(core_test, format_explicitly_convertible_to_std_string_view) {
EXPECT_EQ("foo", // Types explicitly convertible to string_view are not formattable by
fmt::format("{}", explicitly_convertible_to_std_string_view())); // default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_std_string_view>::value,
"");
} }
# endif # endif
#endif #endif
@ -840,48 +943,19 @@ TEST(core_test, adl) {
if (fmt::detail::const_check(true)) return; if (fmt::detail::const_check(true)) return;
auto s = adl_test::string(); auto s = adl_test::string();
char buf[10]; char buf[10];
fmt::format("{}", s); (void)fmt::format("{}", s);
fmt::format_to(buf, "{}", s); fmt::format_to(buf, "{}", s);
fmt::format_to_n(buf, 10, "{}", s); fmt::format_to_n(buf, 10, "{}", s);
fmt::formatted_size("{}", s); (void)fmt::formatted_size("{}", s);
fmt::print("{}", s); fmt::print("{}", s);
fmt::print(stdout, "{}", s); fmt::print(stdout, "{}", s);
} }
struct const_formattable {}; TEST(core_test, has_const_formatter) {
struct nonconst_formattable {}; EXPECT_TRUE((fmt::detail::has_const_formatter<const_formattable,
fmt::format_context>()));
FMT_BEGIN_NAMESPACE EXPECT_FALSE((fmt::detail::has_const_formatter<nonconst_formattable,
template <> struct formatter<const_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(const const_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
template <> struct formatter<nonconst_formattable> {
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(nonconst_formattable&, format_context& ctx)
-> decltype(ctx.out()) {
auto test = string_view("test");
return std::copy_n(test.data(), test.size(), ctx.out());
}
};
FMT_END_NAMESPACE
TEST(core_test, is_const_formattable) {
EXPECT_TRUE((fmt::detail::is_const_formattable<const_formattable,
fmt::format_context>())); fmt::format_context>()));
EXPECT_FALSE((fmt::detail::is_const_formattable<nonconst_formattable,
fmt::format_context>()));
} }
TEST(core_test, format_nonconst) { TEST(core_test, format_nonconst) {

View File

@ -17,12 +17,12 @@
// Exercise the API to verify that everything we expect to can compile. // Exercise the API to verify that everything we expect to can compile.
void test_format_api() { void test_format_api() {
fmt::format(FMT_STRING("{}"), 42); (void)fmt::format(FMT_STRING("{}"), 42);
fmt::format(FMT_STRING(L"{}"), 42); (void)fmt::format(FMT_STRING(L"{}"), 42);
fmt::format(FMT_STRING("noop")); (void)fmt::format(FMT_STRING("noop"));
fmt::to_string(42); (void)fmt::to_string(42);
fmt::to_wstring(42); (void)fmt::to_wstring(42);
std::vector<char> out; std::vector<char> out;
fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42); fmt::format_to(std::back_inserter(out), FMT_STRING("{}"), 42);
@ -35,13 +35,14 @@ void test_format_api() {
} }
void test_chrono() { void test_chrono() {
fmt::format(FMT_STRING("{}"), std::chrono::seconds(42)); (void)fmt::format(FMT_STRING("{}"), std::chrono::seconds(42));
fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42)); (void)fmt::format(FMT_STRING(L"{}"), std::chrono::seconds(42));
} }
void test_text_style() { void test_text_style() {
fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"); fmt::print(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)");
fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"), "rgb(255,20,30)"); (void)fmt::format(fg(fmt::rgb(255, 20, 30)), FMT_STRING("{}"),
"rgb(255,20,30)");
fmt::text_style ts = fg(fmt::rgb(255, 20, 30)); fmt::text_style ts = fg(fmt::rgb(255, 20, 30));
std::string out; std::string out;
@ -51,7 +52,7 @@ void test_text_style() {
void test_range() { void test_range() {
std::vector<char> hello = {'h', 'e', 'l', 'l', 'o'}; std::vector<char> hello = {'h', 'e', 'l', 'l', 'o'};
fmt::format(FMT_STRING("{}"), hello); (void)fmt::format(FMT_STRING("{}"), hello);
} }
int main() { int main() {

View File

@ -1,6 +1,5 @@
#include "fmt/format.h" #include "fmt/format.h"
int main(int argc, char** argv) { int main(int argc, char** argv) {
for(int i = 0; i < argc; ++i) for (int i = 0; i < argc; ++i) fmt::print("{}: {}\n", i, argv[i]);
fmt::print("{}: {}\n", i, argv[i]);
} }

856
vendor/Fmt/test/format vendored
View File

@ -1,856 +0,0 @@
// Formatting library for C++ - the standard API implementation
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_FORMAT_
#define FMT_FORMAT_
#include <algorithm>
#include <cassert>
#include <variant>
#include "fmt/format.h"
// This implementation verifies the correctness of the standard API proposed in
// P0645 Text Formatting and is optimized for copy-pasting from the paper, not
// for efficiency or readability. An efficient implementation should not use
// std::variant and should store packed argument type tags separately from
// values in basic_format_args for small number of arguments.
namespace std {
template<class T>
constexpr bool Integral = is_integral_v<T>;
template <class O>
using iter_difference_t = ptrdiff_t;
}
// https://fmt.dev/Text%20Formatting.html#format.syn
namespace std {
// [format.error], class format_error
class format_error;
// [format.formatter], formatter
template<class charT> class basic_format_parse_context;
using format_parse_context = basic_format_parse_context<char>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
template<class Out, class charT> class basic_format_context;
using format_context = basic_format_context<
/* unspecified */ fmt::detail::buffer_appender<char>, char>;
using wformat_context = basic_format_context<
/* unspecified */ fmt::detail::buffer_appender<wchar_t>, wchar_t>;
template<class T, class charT = char> struct formatter {
formatter() = delete;
};
// [format.arguments], arguments
template<class Context> class basic_format_arg;
template<class Visitor, class Context>
/* see below */ auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg);
template<class Context, class... Args> struct format_arg_store; // exposition only
template<class Context> class basic_format_args;
using format_args = basic_format_args<format_context>;
using wformat_args = basic_format_args<wformat_context>;
template<class Out, class charT>
using format_args_t = basic_format_args<basic_format_context<Out, charT>>;
template<class Context = format_context, class... Args>
format_arg_store<Context, Args...>
make_format_args(const Args&... args);
template<class... Args>
format_arg_store<wformat_context, Args...>
make_wformat_args(const Args&... args);
// [format.functions], formatting functions
template<class... Args>
string format(string_view fmt, const Args&... args);
template<class... Args>
wstring format(wstring_view fmt, const Args&... args);
string vformat(string_view fmt, format_args args);
wstring vformat(wstring_view fmt, wformat_args args);
template<class Out, class... Args>
Out format_to(Out out, string_view fmt, const Args&... args);
template<class Out, class... Args>
Out format_to(Out out, wstring_view fmt, const Args&... args);
template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args);
template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
template<class Out>
struct format_to_n_result {
Out out;
iter_difference_t<Out> size;
};
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
string_view fmt, const Args&... args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
wstring_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(string_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(wstring_view fmt, const Args&... args);
}
// https://fmt.dev/Text%20Formatting.html#format.error
namespace std {
class format_error : public runtime_error {
public:
explicit format_error(const string& what_arg) : runtime_error(what_arg) {}
explicit format_error(const char* what_arg) : runtime_error(what_arg) {}
};
}
namespace std {
namespace detail {
struct error_handler {
// This function is intentionally not constexpr to give a compile-time error.
void on_error(const char* message) {
throw std::format_error(message);
}
};
}
}
// https://fmt.dev/Text%20Formatting.html#format.parse_context
namespace std {
template<class charT>
class basic_format_parse_context {
public:
using char_type = charT;
using const_iterator = typename basic_string_view<charT>::const_iterator;
using iterator = const_iterator;
private:
iterator begin_; // exposition only
iterator end_; // exposition only
enum indexing { unknown, manual, automatic }; // exposition only
indexing indexing_; // exposition only
size_t next_arg_id_; // exposition only
size_t num_args_; // exposition only
public:
explicit constexpr basic_format_parse_context(basic_string_view<charT> fmt,
size_t num_args = 0) noexcept;
basic_format_parse_context(const basic_format_parse_context&) = delete;
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
constexpr const_iterator begin() const noexcept;
constexpr const_iterator end() const noexcept;
constexpr void advance_to(const_iterator it);
constexpr size_t next_arg_id();
constexpr void check_arg_id(size_t id);
// Implementation detail:
constexpr void check_arg_id(fmt::string_view) {}
detail::error_handler error_handler() const { return {}; }
void on_error(const char* msg) { error_handler().on_error(msg); }
};
}
namespace std {
template<class charT>
/* explicit */ constexpr basic_format_parse_context<charT>::
basic_format_parse_context(basic_string_view<charT> fmt,
size_t num_args) noexcept
: begin_(fmt.begin()), end_(fmt.end()), indexing_(unknown), next_arg_id_(0), num_args_(num_args) {}
template<class charT>
constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::begin() const noexcept { return begin_; }
template<class charT>
constexpr typename basic_format_parse_context<charT>::const_iterator basic_format_parse_context<charT>::end() const noexcept { return end_; }
template<class charT>
constexpr void basic_format_parse_context<charT>::advance_to(typename basic_format_parse_context<charT>::iterator it) { begin_ = it; }
template<class charT>
constexpr size_t basic_format_parse_context<charT>::next_arg_id() {
if (indexing_ == manual)
throw format_error("manual to automatic indexing");
if (indexing_ == unknown)
indexing_ = automatic;
return next_arg_id_++;
}
template<class charT>
constexpr void basic_format_parse_context<charT>::check_arg_id(size_t id) {
// clang doesn't support __builtin_is_constant_evaluated yet
//if (!(!__builtin_is_constant_evaluated() || id < num_args_))
// throw format_error(invalid index is out of range");
if (indexing_ == automatic)
throw format_error("automatic to manual indexing");
if (indexing_ == unknown)
indexing_ = manual;
}
}
// https://fmt.dev/Text%20Formatting.html#format.context
namespace std {
template<class Out, class charT>
class basic_format_context {
basic_format_args<basic_format_context> args_; // exposition only
Out out_; // exposition only
public:
using iterator = Out;
using char_type = charT;
template<class T> using formatter_type = formatter<T, charT>;
basic_format_arg<basic_format_context> arg(size_t id) const;
iterator out();
void advance_to(iterator it);
// Implementation details:
using format_arg = basic_format_arg<basic_format_context>;
basic_format_context(Out out, basic_format_args<basic_format_context> args, fmt::detail::locale_ref)
: args_(args), out_(out) {}
detail::error_handler error_handler() const { return {}; }
basic_format_arg<basic_format_context> arg(fmt::basic_string_view<charT>) const {
return {}; // unused: named arguments are not supported yet
}
void on_error(const char* msg) { error_handler().on_error(msg); }
};
}
namespace std {
template<class O, class charT>
basic_format_arg<basic_format_context<O, charT>> basic_format_context<O, charT>::arg(size_t id) const { return args_.get(id); }
template<class O, class charT>
typename basic_format_context<O, charT>::iterator basic_format_context<O, charT>::out() { return out_; }
template<class O, class charT>
void basic_format_context<O, charT>::advance_to(typename basic_format_context<O, charT>::iterator it) { out_ = it; }
}
namespace std {
namespace detail {
template <typename T>
constexpr bool is_standard_integer_v =
std::is_same_v<T, signed char> ||
std::is_same_v<T, short int> ||
std::is_same_v<T, int> ||
std::is_same_v<T, long int> ||
std::is_same_v<T, long long int>;
template <typename T>
constexpr bool is_standard_unsigned_integer_v =
std::is_same_v<T, unsigned char> ||
std::is_same_v<T, unsigned short int> ||
std::is_same_v<T, unsigned int> ||
std::is_same_v<T, unsigned long int> ||
std::is_same_v<T, unsigned long long int>;
template <typename T, typename Char> struct formatter;
}
}
// https://fmt.dev/Text%20Formatting.html#format.arg
namespace std {
template<class Context>
class basic_format_arg {
public:
class handle;
private:
using char_type = typename Context::char_type; // exposition only
variant<monostate, bool, char_type,
int, unsigned int, long long int, unsigned long long int,
double, long double,
const char_type*, basic_string_view<char_type>,
const void*, handle> value; // exposition only
template<typename T,
typename = enable_if_t<
std::is_same_v<T, bool> ||
std::is_same_v<T, char_type> ||
(std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>) ||
detail::is_standard_integer_v<T> ||
detail::is_standard_unsigned_integer_v<T> ||
sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0
>> explicit basic_format_arg(const T& v) noexcept; // exposition only
explicit basic_format_arg(float n) noexcept; // exposition only
explicit basic_format_arg(double n) noexcept; // exposition only
explicit basic_format_arg(long double n) noexcept; // exposition only
explicit basic_format_arg(const char_type* s); // exposition only
template<class traits>
explicit basic_format_arg(
basic_string_view<char_type, traits> s) noexcept; // exposition only
template<class traits, class Allocator>
explicit basic_format_arg(
const basic_string<char_type, traits, Allocator>& s) noexcept; // exposition only
explicit basic_format_arg(nullptr_t) noexcept; // exposition only
template<class T, typename = enable_if_t<is_void_v<T>>>
explicit basic_format_arg(const T* p) noexcept; // exposition only
// Fails due to a bug in clang
//template<class Visitor, class Ctx>
// friend auto visit_format_arg(Visitor&& vis,
// basic_format_arg<Ctx> arg); // exposition only
friend auto get_value(basic_format_arg arg) {
return arg.value;
}
template <typename T, typename Char> friend struct detail::formatter;
template<class Ctx, class... Args>
friend format_arg_store<Ctx, Args...>
make_format_args(const Args&... args); // exposition only
public:
basic_format_arg() noexcept;
explicit operator bool() const noexcept;
};
}
namespace std {
template<class Context>
basic_format_arg<Context>::basic_format_arg() noexcept {}
template<class Context>
template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T& v) noexcept {
if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, char_type>)
value = v;
else if constexpr (std::is_same_v<T, char> && std::is_same_v<char_type, wchar_t>)
value = static_cast<wchar_t>(v);
else if constexpr (detail::is_standard_integer_v<T> && sizeof(T) <= sizeof(int))
value = static_cast<int>(v);
else if constexpr (detail::is_standard_unsigned_integer_v<T> && sizeof(T) <= sizeof(unsigned))
value = static_cast<unsigned>(v);
else if constexpr (detail::is_standard_integer_v<T>)
value = static_cast<long long int>(v);
else if constexpr (detail::is_standard_unsigned_integer_v<T>)
value = static_cast<unsigned long long int>(v);
else if constexpr (sizeof(typename Context::template formatter_type<T>().format(declval<const T&>(), declval<Context&>())) != 0)
value = handle(v);
}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(float n) noexcept
: value(static_cast<double>(n)) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(double n) noexcept
: value(n) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(long double n) noexcept
: value(n) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(const typename basic_format_arg<Context>::char_type* s)
: value(s) {
assert(s != nullptr);
}
template<class Context>
template<class traits>
/* explicit */ basic_format_arg<Context>::basic_format_arg(basic_string_view<char_type, traits> s) noexcept
: value(s) {}
template<class Context>
template<class traits, class Allocator>
/* explicit */ basic_format_arg<Context>::basic_format_arg(
const basic_string<char_type, traits, Allocator>& s) noexcept
: value(basic_string_view<char_type>(s.data(), s.size())) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::basic_format_arg(nullptr_t) noexcept
: value(static_cast<const void*>(nullptr)) {}
template<class Context>
template<class T, typename> /* explicit */ basic_format_arg<Context>::basic_format_arg(const T* p) noexcept
: value(p) {}
template<class Context>
/* explicit */ basic_format_arg<Context>::operator bool() const noexcept {
return !holds_alternative<monostate>(value);
}
}
namespace std {
template<class Context>
class basic_format_arg<Context>::handle {
const void* ptr_; // exposition only
void (*format_)(basic_format_parse_context<char_type>&,
Context&, const void*); // exposition only
template<class T> explicit handle(const T& val) noexcept; // exposition only
friend class basic_format_arg<Context>; // exposition only
public:
void format(basic_format_parse_context<char_type>&, Context& ctx) const;
};
}
namespace std {
template<class Context>
template<class T> /* explicit */ basic_format_arg<Context>::handle::handle(const T& val) noexcept
: ptr_(&val), format_([](basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx, const void* ptr) {
typename Context::template formatter_type<T> f;
parse_ctx.advance_to(f.parse(parse_ctx));
format_ctx.advance_to(f.format(*static_cast<const T*>(ptr), format_ctx));
}) {}
template<class Context>
void basic_format_arg<Context>::handle::format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx) const {
format_(parse_ctx, format_ctx, ptr_);
}
// https://fmt.dev/Text%20Formatting.html#format.visit
template<class Visitor, class Context>
auto visit_format_arg(Visitor&& vis, basic_format_arg<Context> arg) {
return visit(vis, get_value(arg));
}
}
// https://fmt.dev/Text%20Formatting.html#format.store
namespace std {
template<class Context, class... Args>
struct format_arg_store { // exposition only
array<basic_format_arg<Context>, sizeof...(Args)> args;
};
}
// https://fmt.dev/Text%20Formatting.html#format.basic_args
namespace std {
template<class Context>
class basic_format_args {
size_t size_; // exposition only
const basic_format_arg<Context>* data_; // exposition only
public:
basic_format_args() noexcept;
template<class... Args>
basic_format_args(const format_arg_store<Context, Args...>& store) noexcept;
basic_format_arg<Context> get(size_t i) const noexcept;
};
}
namespace std {
template<class Context>
basic_format_args<Context>::basic_format_args() noexcept : size_(0) {}
template<class Context>
template<class... Args>
basic_format_args<Context>::basic_format_args(const format_arg_store<Context, Args...>& store) noexcept
: size_(sizeof...(Args)), data_(store.args.data()) {}
template<class Context>
basic_format_arg<Context> basic_format_args<Context>::get(size_t i) const noexcept {
return i < size_ ? data_[i] : basic_format_arg<Context>();
}
}
namespace std {
// https://fmt.dev/Text%20Formatting.html#format.make_args
template<class Context /*= format_context*/, class... Args>
format_arg_store<Context, Args...> make_format_args(const Args&... args) {
return {basic_format_arg<Context>(args)...};
}
// https://fmt.dev/Text%20Formatting.html#format.make_wargs
template<class... Args>
format_arg_store<wformat_context, Args...> make_wformat_args(const Args&... args) {
return make_format_args<wformat_context>(args...);
}
}
namespace std {
namespace detail {
template <typename OutputIt, typename Char>
class arg_formatter
: public fmt::detail::arg_formatter_base<OutputIt, Char, error_handler> {
private:
using char_type = Char;
using base = fmt::detail::arg_formatter_base<OutputIt, Char, error_handler>;
using format_context = std::basic_format_context<OutputIt, Char>;
using parse_context = basic_format_parse_context<Char>;
parse_context* parse_ctx_;
format_context& ctx_;
public:
using iterator = OutputIt;
using format_specs = typename base::format_specs;
/**
\rst
Constructs an argument formatter object.
*ctx* is a reference to the formatting context,
*spec* contains format specifier information for standard argument types.
\endrst
*/
arg_formatter(format_context& ctx, parse_context* parse_ctx = nullptr, fmt::format_specs* spec = nullptr)
: base(ctx.out(), spec, {}), parse_ctx_(parse_ctx), ctx_(ctx) {}
using base::operator();
/** Formats an argument of a user-defined type. */
iterator operator()(typename std::basic_format_arg<format_context>::handle handle) {
handle.format(*parse_ctx_, ctx_);
return this->out();
}
iterator operator()(monostate) {
throw format_error("");
}
};
template <typename Context>
inline fmt::detail::type get_type(basic_format_arg<Context> arg) {
return visit_format_arg([&] (auto val) {
using char_type = typename Context::char_type;
using T = decltype(val);
if (std::is_same_v<T, monostate>)
return fmt::detail::type::none_type;
if (std::is_same_v<T, bool>)
return fmt::detail::type::bool_type;
if (std::is_same_v<T, char_type>)
return fmt::detail::type::char_type;
if (std::is_same_v<T, int>)
return fmt::detail::type::int_type;
if (std::is_same_v<T, unsigned int>)
return fmt::detail::type::uint_type;
if (std::is_same_v<T, long long int>)
return fmt::detail::type::long_long_type;
if (std::is_same_v<T, unsigned long long int>)
return fmt::detail::type::ulong_long_type;
if (std::is_same_v<T, double>)
return fmt::detail::type::double_type;
if (std::is_same_v<T, long double>)
return fmt::detail::type::long_double_type;
if (std::is_same_v<T, const char_type*>)
return fmt::detail::type::cstring_type;
if (std::is_same_v<T, basic_string_view<char_type>>)
return fmt::detail::type::string_type;
if (std::is_same_v<T, const void*>)
return fmt::detail::type::pointer_type;
assert(get_value(arg).index() == 12);
return fmt::detail::type::custom_type;
}, arg);
}
template <typename Context>
class custom_formatter {
private:
using parse_context = basic_format_parse_context<typename Context::char_type>;
parse_context& parse_ctx_;
Context& format_ctx_;
public:
custom_formatter(parse_context& parse_ctx, Context& ctx) : parse_ctx_(parse_ctx), format_ctx_(ctx) {}
bool operator()(typename basic_format_arg<Context>::handle h) const {
h.format(parse_ctx_, format_ctx_);
return true;
}
template <typename T> bool operator()(T) const { return false; }
};
template <typename ArgFormatter, typename Char, typename Context>
struct format_handler : detail::error_handler {
using iterator = typename ArgFormatter::iterator;
format_handler(iterator out, basic_string_view<Char> str,
basic_format_args<Context> format_args,
fmt::detail::locale_ref loc)
: parse_ctx(str), context(out, format_args, loc) {}
void on_text(const Char* begin, const Char* end) {
auto size = fmt::detail::to_unsigned(end - begin);
auto out = context.out();
auto&& it = fmt::detail::reserve(out, size);
it = std::copy_n(begin, size, it);
context.advance_to(out);
}
int on_arg_id() { return parse_ctx.next_arg_id(); }
int on_arg_id(unsigned id) { return parse_ctx.check_arg_id(id), id; }
int on_arg_id(fmt::basic_string_view<Char>) { return 0; }
void on_replacement_field(int id, const Char* p) {
auto arg = context.arg(id);
parse_ctx.advance_to(parse_ctx.begin() + (p - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context);
if (!visit_format_arg(f, arg))
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx), arg));
}
const Char* on_format_specs(int id, const Char* begin, const Char* end) {
auto arg = context.arg(id);
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context);
if (visit_format_arg(f, arg)) return &*parse_ctx.begin();
fmt::basic_format_specs<Char> specs;
using fmt::detail::specs_handler;
using parse_context = basic_format_parse_context<Char>;
fmt::detail::specs_checker<specs_handler<parse_context, Context>> handler(
specs_handler<parse_context, Context>(specs, parse_ctx, context), get_type(arg));
begin = parse_format_specs(begin, end, handler);
if (begin == end || *begin != '}') on_error("missing '}' in format string");
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx, &specs), arg));
return begin;
}
basic_format_parse_context<Char> parse_ctx;
Context context;
};
template <typename T, typename Char>
struct formatter {
// Parses format specifiers stopping either at the end of the range or at the
// terminating '}'.
template <typename ParseContext>
FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) {
namespace detail = fmt::detail;
typedef detail::dynamic_specs_handler<ParseContext> handler_type;
auto type = detail::mapped_type_constant<T, fmt::buffer_context<Char>>::value;
detail::specs_checker<handler_type> handler(handler_type(specs_, ctx),
type);
auto it = parse_format_specs(ctx.begin(), ctx.end(), handler);
auto type_spec = specs_.type;
auto eh = ctx.error_handler();
switch (type) {
case detail::type::none_type:
FMT_ASSERT(false, "invalid argument type");
break;
case detail::type::int_type:
case detail::type::uint_type:
case detail::type::long_long_type:
case detail::type::ulong_long_type:
case detail::type::bool_type:
handle_int_type_spec(type_spec,
detail::int_type_checker<decltype(eh)>(eh));
break;
case detail::type::char_type:
handle_char_specs(
&specs_, detail::char_specs_checker<decltype(eh)>(type_spec, eh));
break;
case detail::type::double_type:
case detail::type::long_double_type:
detail::parse_float_type_spec(specs_, eh);
break;
case detail::type::cstring_type:
detail::handle_cstring_type_spec(
type_spec, detail::cstring_type_checker<decltype(eh)>(eh));
break;
case detail::type::string_type:
detail::check_string_type_spec(type_spec, eh);
break;
case detail::type::pointer_type:
detail::check_pointer_type_spec(type_spec, eh);
break;
case detail::type::custom_type:
// Custom format specifiers should be checked in parse functions of
// formatter specializations.
break;
}
return it;
}
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
fmt::detail::handle_dynamic_spec<fmt::detail::width_checker>(
specs_.width, specs_.width_ref, ctx);
fmt::detail::handle_dynamic_spec<fmt::detail::precision_checker>(
specs_.precision, specs_.precision_ref, ctx);
using af = arg_formatter<typename FormatContext::iterator,
typename FormatContext::char_type>;
return visit_format_arg(af(ctx, nullptr, &specs_),
basic_format_arg<FormatContext>(val));
}
private:
fmt::detail::dynamic_format_specs<Char> specs_;
};
} // namespace detail
// https://fmt.dev/Text%20Formatting.html#format.functions
template<class... Args>
string format(string_view fmt, const Args&... args) {
return vformat(fmt, make_format_args(args...));
}
template<class... Args>
wstring format(wstring_view fmt, const Args&... args) {
return vformat(fmt, make_wformat_args(args...));
}
string vformat(string_view fmt, format_args args) {
fmt::memory_buffer mbuf;
fmt::detail::buffer<char>& buf = mbuf;
using af = detail::arg_formatter<fmt::format_context::iterator, char>;
detail::format_handler<af, char, format_context>
h(fmt::detail::buffer_appender<char>(buf), fmt, args, {});
fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
return to_string(mbuf);
}
wstring vformat(wstring_view fmt, wformat_args args);
template<class Out, class... Args>
Out format_to(Out out, string_view fmt, const Args&... args) {
using context = basic_format_context<Out, decltype(fmt)::value_type>;
return vformat_to(out, fmt, make_format_args<context>(args...));
}
template<class Out, class... Args>
Out format_to(Out out, wstring_view fmt, const Args&... args) {
using context = basic_format_context<Out, decltype(fmt)::value_type>;
return vformat_to(out, fmt, make_format_args<context>(args...));
}
template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args) {
using af = detail::arg_formatter<Out, char>;
detail::format_handler<af, char, basic_format_context<Out, char>>
h(out, fmt, args, {});
fmt::detail::parse_format_string<false>(fmt::to_string_view(fmt), h);
return h.context.out();
}
template<class Out>
Out vformat_to(Out out, wstring_view fmt, format_args_t<fmt::type_identity_t<Out>, wchar_t> args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
string_view fmt, const Args&... args);
template<class Out, class... Args>
format_to_n_result<Out> format_to_n(Out out, iter_difference_t<Out> n,
wstring_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(string_view fmt, const Args&... args);
template<class... Args>
size_t formatted_size(wstring_view fmt, const Args&... args);
#define charT char
template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {};
template<> struct formatter<char, wchar_t>;
template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {};
template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {};
template<size_t N> struct formatter<const charT[N], charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits, class Allocator>
struct formatter<basic_string<charT, traits, Allocator>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits>
struct formatter<basic_string_view<charT, traits>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {};
template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {};
template <> struct formatter<short, charT> : detail::formatter<int, charT> {};
template <> struct formatter<int, charT> : detail::formatter<int, charT> {};
template <> struct formatter<long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {};
template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {};
template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {};
template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {};
template <> struct formatter<float, charT> : detail::formatter<double, charT> {};
template <> struct formatter<double, charT> : detail::formatter<double, charT> {};
template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {};
#undef charT
#define charT wchar_t
template<> struct formatter<charT, charT> : detail::formatter<charT, charT> {};
template<> struct formatter<char, wchar_t> : detail::formatter<charT, charT> {};
template<> struct formatter<charT*, charT> : detail::formatter<const charT*, charT> {};
template<> struct formatter<const charT*, charT> : detail::formatter<const charT*, charT> {};
template<size_t N> struct formatter<const charT[N], charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits, class Allocator>
struct formatter<std::basic_string<charT, traits, Allocator>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template<class traits>
struct formatter<std::basic_string_view<charT, traits>, charT>
: detail::formatter<std::basic_string_view<charT>, charT> {};
template <> struct formatter<nullptr_t, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<const void*, charT> : detail::formatter<const void*, charT> {};
template <> struct formatter<bool, charT> : detail::formatter<bool, charT> {};
template <> struct formatter<signed char, charT> : detail::formatter<int, charT> {};
template <> struct formatter<short, charT> : detail::formatter<int, charT> {};
template <> struct formatter<int, charT> : detail::formatter<int, charT> {};
template <> struct formatter<long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), int, long long>, charT> {};
template <> struct formatter<long long, charT> : detail::formatter<long long, charT> {};
template <> struct formatter<unsigned char, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned short, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned int, charT> : detail::formatter<unsigned int, charT> {};
template <> struct formatter<unsigned long, charT>
: detail::formatter<std::conditional_t<sizeof(long) == sizeof(int), unsigned, unsigned long long>, charT> {};
template <> struct formatter<unsigned long long, charT> : detail::formatter<unsigned long long, charT> {};
template <> struct formatter<float, charT> : detail::formatter<double, charT> {};
template <> struct formatter<double, charT> : detail::formatter<double, charT> {};
template <> struct formatter<long double, charT> : detail::formatter<long double, charT> {};
#undef charT
template<> struct formatter<const wchar_t, char> {
formatter() = delete;
};
}
#endif // FMT_FORMAT_

View File

@ -96,23 +96,6 @@ TEST(bigint_test, multiply) {
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax)); EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
} }
TEST(bigint_test, accumulator) {
fmt::detail::accumulator acc;
EXPECT_EQ(acc.lower, 0);
EXPECT_EQ(acc.upper, 0);
acc.upper = 12;
acc.lower = 34;
EXPECT_EQ(static_cast<uint32_t>(acc), 34);
acc += 56;
EXPECT_EQ(acc.lower, 90);
acc += max_value<uint64_t>();
EXPECT_EQ(acc.upper, 13);
EXPECT_EQ(acc.lower, 89);
acc >>= 32;
EXPECT_EQ(acc.upper, 0);
EXPECT_EQ(acc.lower, 13 * 0x100000000);
}
TEST(bigint_test, square) { TEST(bigint_test, square) {
bigint n0(0); bigint n0(0);
n0.square(); n0.square();
@ -207,14 +190,14 @@ TEST(fp_test, get_cached_power) {
using limits = std::numeric_limits<double>; using limits = std::numeric_limits<double>;
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) { for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
int dec_exp = 0; int dec_exp = 0;
auto fp = fmt::detail::get_cached_power(exp, dec_exp); auto power = fmt::detail::get_cached_power(exp, dec_exp);
bigint exact, cache(fp.f); bigint exact, cache(power.f);
if (dec_exp >= 0) { if (dec_exp >= 0) {
exact.assign_pow10(dec_exp); exact.assign_pow10(dec_exp);
if (fp.e <= 0) if (power.e <= 0)
exact <<= -fp.e; exact <<= -power.e;
else else
cache <<= fp.e; cache <<= power.e;
exact.align(cache); exact.align(cache);
cache.align(exact); cache.align(exact);
auto exact_str = fmt::format("{}", exact); auto exact_str = fmt::format("{}", exact);
@ -228,9 +211,9 @@ TEST(fp_test, get_cached_power) {
EXPECT_EQ(diff, 0); EXPECT_EQ(diff, 0);
} else { } else {
cache.assign_pow10(-dec_exp); cache.assign_pow10(-dec_exp);
cache *= fp.f + 1; // Inexact check. cache *= power.f + 1; // Inexact check.
exact.assign(1); exact.assign(1);
exact <<= -fp.e; exact <<= -power.e;
exact.align(cache); exact.align(cache);
auto exact_str = fmt::format("{}", exact); auto exact_str = fmt::format("{}", exact);
auto cache_str = fmt::format("{}", cache); auto cache_str = fmt::format("{}", cache);
@ -243,14 +226,17 @@ TEST(fp_test, get_cached_power) {
TEST(fp_test, dragonbox_max_k) { TEST(fp_test, dragonbox_max_k) {
using fmt::detail::dragonbox::floor_log10_pow2; using fmt::detail::dragonbox::floor_log10_pow2;
using float_info = fmt::detail::dragonbox::float_info<float>; using float_info = fmt::detail::dragonbox::float_info<float>;
EXPECT_EQ(fmt::detail::const_check(float_info::max_k), EXPECT_EQ(
float_info::kappa - floor_log10_pow2(float_info::min_exponent - fmt::detail::const_check(float_info::max_k),
float_info::significand_bits)); float_info::kappa -
floor_log10_pow2(std::numeric_limits<float>::min_exponent -
fmt::detail::num_significand_bits<float>() - 1));
using double_info = fmt::detail::dragonbox::float_info<double>; using double_info = fmt::detail::dragonbox::float_info<double>;
EXPECT_EQ( EXPECT_EQ(
fmt::detail::const_check(double_info::max_k), fmt::detail::const_check(double_info::max_k),
double_info::kappa - floor_log10_pow2(double_info::min_exponent - double_info::kappa -
double_info::significand_bits)); floor_log10_pow2(std::numeric_limits<double>::min_exponent -
fmt::detail::num_significand_bits<double>() - 1));
} }
TEST(fp_test, get_round_direction) { TEST(fp_test, get_round_direction) {
@ -278,25 +264,22 @@ TEST(fp_test, get_round_direction) {
} }
TEST(fp_test, fixed_handler) { TEST(fp_test, fixed_handler) {
struct handler : fmt::detail::fixed_handler { struct handler : fmt::detail::gen_digits_handler {
char buffer[10]; char buffer[10];
handler(int prec = 0) : fmt::detail::fixed_handler() { handler(int prec = 0) : fmt::detail::gen_digits_handler() {
buf = buffer; buf = buffer;
precision = prec; precision = prec;
} }
}; };
int exp = 0; handler().on_digit('0', 100, 99, 0, false);
handler().on_digit('0', 100, 99, 0, exp, false); EXPECT_THROW(handler().on_digit('0', 100, 100, 0, false), assertion_failure);
EXPECT_THROW(handler().on_digit('0', 100, 100, 0, exp, false),
assertion_failure);
namespace digits = fmt::detail::digits; namespace digits = fmt::detail::digits;
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::error); EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, false), digits::error);
// Check that divisor - error doesn't overflow. // Check that divisor - error doesn't overflow.
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error); EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, false), digits::error);
// Check that 2 * error doesn't overflow. // Check that 2 * error doesn't overflow.
uint64_t max = max_value<uint64_t>(); uint64_t max = max_value<uint64_t>();
EXPECT_EQ(handler(1).on_digit('0', max, 10, max - 1, exp, false), EXPECT_EQ(handler(1).on_digit('0', max, 10, max - 1, false), digits::error);
digits::error);
} }
TEST(fp_test, grisu_format_compiles_with_on_ieee_double) { TEST(fp_test, grisu_format_compiles_with_on_ieee_double) {
@ -360,14 +343,6 @@ TEST(format_impl_test, count_digits) {
test_count_digits<uint64_t>(); test_count_digits<uint64_t>();
} }
TEST(format_impl_test, write_fallback_uintptr) {
std::string s;
fmt::detail::write_ptr<char>(
std::back_inserter(s),
fmt::detail::fallback_uintptr(reinterpret_cast<void*>(0xface)), nullptr);
EXPECT_EQ(s, "0xface");
}
#ifdef _WIN32 #ifdef _WIN32
# include <windows.h> # include <windows.h>
#endif #endif

View File

@ -39,6 +39,75 @@ using testing::StrictMock;
enum { buffer_size = 256 }; enum { buffer_size = 256 };
TEST(uint128_test, ctor) {
using fmt::detail::uint128_fallback;
auto n = uint128_fallback();
EXPECT_EQ(n, 0);
n = uint128_fallback(42);
EXPECT_EQ(n, 42);
EXPECT_EQ(static_cast<uint64_t>(n), 42);
}
TEST(uint128_test, shift) {
auto n = fmt::detail::uint128_fallback(42);
n = n << 64;
EXPECT_EQ(static_cast<uint64_t>(n), 0);
n = n >> 64;
EXPECT_EQ(static_cast<uint64_t>(n), 42);
n = n << 62;
EXPECT_EQ(static_cast<uint64_t>(n >> 64), 0xa);
EXPECT_EQ(static_cast<uint64_t>(n), 0x8000000000000000);
n = n >> 62;
EXPECT_EQ(static_cast<uint64_t>(n), 42);
}
TEST(uint128_test, minus) {
auto n = fmt::detail::uint128_fallback(42);
EXPECT_EQ(n - 2, 40);
}
template <typename Float> void check_isfinite() {
using fmt::detail::isfinite;
EXPECT_TRUE(isfinite(Float(0.0)));
EXPECT_TRUE(isfinite(Float(42.0)));
EXPECT_TRUE(isfinite(Float(-42.0)));
EXPECT_TRUE(isfinite(Float(fmt::detail::max_value<double>())));
// Use double because std::numeric_limits is broken for __float128.
using limits = std::numeric_limits<double>;
EXPECT_FALSE(isfinite(Float(limits::infinity())));
EXPECT_FALSE(isfinite(Float(-limits::infinity())));
EXPECT_FALSE(isfinite(Float(limits::quiet_NaN())));
EXPECT_FALSE(isfinite(Float(-limits::quiet_NaN())));
}
TEST(float_test, isfinite) {
check_isfinite<double>();
#ifdef __SIZEOF_FLOAT128__
check_isfinite<fmt::detail::float128>();
#endif
}
template <typename Float> void check_isnan() {
using fmt::detail::isnan;
EXPECT_FALSE(isnan(Float(0.0)));
EXPECT_FALSE(isnan(Float(42.0)));
EXPECT_FALSE(isnan(Float(-42.0)));
EXPECT_FALSE(isnan(Float(fmt::detail::max_value<double>())));
// Use double because std::numeric_limits is broken for __float128.
using limits = std::numeric_limits<double>;
EXPECT_FALSE(isnan(Float(limits::infinity())));
EXPECT_FALSE(isnan(Float(-limits::infinity())));
EXPECT_TRUE(isnan(Float(limits::quiet_NaN())));
EXPECT_TRUE(isnan(Float(-limits::quiet_NaN())));
}
TEST(float_test, isnan) {
check_isnan<double>();
#ifdef __SIZEOF_FLOAT128__
check_isnan<fmt::detail::float128>();
#endif
}
struct uint32_pair { struct uint32_pair {
uint32_t u[2]; uint32_t u[2];
}; };
@ -223,8 +292,9 @@ TEST(memory_buffer_test, move_ctor_dynamic_buffer) {
buffer.push_back('a'); buffer.push_back('a');
basic_memory_buffer<char, 4, std_allocator> buffer2(std::move(buffer)); basic_memory_buffer<char, 4, std_allocator> buffer2(std::move(buffer));
// Move should rip the guts of the first buffer. // Move should rip the guts of the first buffer.
EXPECT_EQ(inline_buffer_ptr, &buffer[0]); EXPECT_EQ(&buffer[0], inline_buffer_ptr);
EXPECT_EQ("testa", std::string(&buffer2[0], buffer2.size())); EXPECT_EQ(buffer.size(), 0);
EXPECT_EQ(std::string(&buffer2[0], buffer2.size()), "testa");
EXPECT_GT(buffer2.capacity(), 4u); EXPECT_GT(buffer2.capacity(), 4u);
} }
@ -325,7 +395,7 @@ template <typename Allocator, size_t MaxSize>
class max_size_allocator : public Allocator { class max_size_allocator : public Allocator {
public: public:
using typename Allocator::value_type; using typename Allocator::value_type;
size_t max_size() const FMT_NOEXCEPT { return MaxSize; } size_t max_size() const noexcept { return MaxSize; }
value_type* allocate(size_t n) { value_type* allocate(size_t n) {
if (n > max_size()) { if (n > max_size()) {
throw std::length_error("size > max_size"); throw std::length_error("size > max_size");
@ -370,11 +440,11 @@ TEST(format_test, escape) {
} }
TEST(format_test, unmatched_braces) { TEST(format_test, unmatched_braces) {
EXPECT_THROW_MSG(fmt::format(runtime("{")), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{")), format_error,
"invalid format string"); "invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("}")), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("}")), format_error,
"unmatched '}' in format string"); "unmatched '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0{}")), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0{}")), format_error,
"invalid format string"); "invalid format string");
} }
@ -391,30 +461,30 @@ TEST(format_test, args_in_different_positions) {
} }
TEST(format_test, arg_errors) { TEST(format_test, arg_errors) {
EXPECT_THROW_MSG(fmt::format(runtime("{")), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{")), format_error,
"invalid format string"); "invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{?}")), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{?}")), format_error,
"invalid format string"); "invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0")), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0")), format_error,
"invalid format string"); "invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0}")), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0}")), format_error,
"argument not found"); "argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{00}"), 42), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{00}"), 42), format_error,
"invalid format string"); "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(fmt::format(runtime(format_str)), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
"invalid format string"); "invalid format string");
safe_sprintf(format_str, "{%u}", INT_MAX); safe_sprintf(format_str, "{%u}", INT_MAX);
EXPECT_THROW_MSG(fmt::format(runtime(format_str)), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
"argument not found"); "argument not found");
safe_sprintf(format_str, "{%u", INT_MAX + 1u); safe_sprintf(format_str, "{%u", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str)), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
"invalid format string"); "invalid format string");
safe_sprintf(format_str, "{%u}", INT_MAX + 1u); safe_sprintf(format_str, "{%u}", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str)), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
"argument not found"); "argument not found");
} }
@ -458,24 +528,26 @@ TEST(format_test, named_arg) {
fmt::arg("i", 0), fmt::arg("j", 0), fmt::arg("k", 0), fmt::arg("i", 0), fmt::arg("j", 0), fmt::arg("k", 0),
fmt::arg("l", 0), fmt::arg("m", 0), fmt::arg("n", 0), fmt::arg("l", 0), fmt::arg("m", 0), fmt::arg("n", 0),
fmt::arg("o", 0), fmt::arg("p", 0))); fmt::arg("o", 0), fmt::arg("p", 0)));
EXPECT_THROW_MSG(fmt::format(runtime("{a}")), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{a}")), format_error,
"argument not found"); "argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{a}"), 42), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{a}"), 42), format_error,
"argument not found"); "argument not found");
} }
TEST(format_test, auto_arg_index) { TEST(format_test, auto_arg_index) {
EXPECT_EQ("abc", fmt::format("{}{}{}", 'a', 'b', 'c')); EXPECT_EQ("abc", fmt::format("{}{}{}", 'a', 'b', 'c'));
EXPECT_THROW_MSG(fmt::format(runtime("{0}{}"), 'a', 'b'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0}{}"), 'a', 'b'), format_error,
"cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{}{0}"), 'a', 'b'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{}{0}"), 'a', 'b'), format_error,
"cannot switch from automatic to manual argument indexing"); "cannot switch from automatic to manual argument indexing");
EXPECT_EQ("1.2", fmt::format("{:.{}}", 1.2345, 2)); EXPECT_EQ("1.2", fmt::format("{:.{}}", 1.2345, 2));
EXPECT_THROW_MSG(fmt::format(runtime("{0}:.{}"), 1.2345, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0}:.{}"), 1.2345, 2),
format_error,
"cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{:.{0}}"), 1.2345, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:.{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(fmt::format(runtime("{}")), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{}")), format_error,
"argument not found"); "argument not found");
} }
@ -533,9 +605,9 @@ TEST(format_test, center_align) {
} }
TEST(format_test, fill) { TEST(format_test, fill) {
EXPECT_THROW_MSG(fmt::format(runtime("{0:{<5}"), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{<5}"), 'c'), format_error,
"invalid fill character '{'"); "invalid fill character '{'");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{<5}}"), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{<5}}"), 'c'), format_error,
"invalid fill character '{'"); "invalid fill character '{'");
EXPECT_EQ("**42", fmt::format("{0:*>4}", 42)); EXPECT_EQ("**42", fmt::format("{0:*>4}", 42));
EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42)); EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42));
@ -554,80 +626,86 @@ TEST(format_test, fill) {
EXPECT_EQ(std::string("\0\0\0*", 4), EXPECT_EQ(std::string("\0\0\0*", 4),
fmt::format(string_view("{:\0>4}", 6), '*')); fmt::format(string_view("{:\0>4}", 6), '*'));
EXPECT_EQ("жж42", fmt::format("{0:ж>4}", 42)); EXPECT_EQ("жж42", fmt::format("{0:ж>4}", 42));
EXPECT_THROW_MSG(fmt::format(runtime("{:\x80\x80\x80\x80\x80>}"), 0), EXPECT_THROW_MSG((void)fmt::format(runtime("{:\x80\x80\x80\x80\x80>}"), 0),
format_error, "missing '}' in format string"); format_error, "invalid type specifier");
} }
TEST(format_test, plus_sign) { TEST(format_test, plus_sign) {
EXPECT_EQ("+42", fmt::format("{0:+}", 42)); EXPECT_EQ("+42", fmt::format("{0:+}", 42));
EXPECT_EQ("-42", fmt::format("{0:+}", -42)); EXPECT_EQ("-42", fmt::format("{0:+}", -42));
EXPECT_EQ("+42", fmt::format("{0:+}", 42)); EXPECT_EQ("+42", fmt::format("{0:+}", 42));
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 42u), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42u), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("+42", fmt::format("{0:+}", 42l)); EXPECT_EQ("+42", fmt::format("{0:+}", 42l));
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 42ul), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("+42", fmt::format("{0:+}", 42ll)); EXPECT_EQ("+42", fmt::format("{0:+}", 42ll));
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 42ull), format_error, #if FMT_USE_INT128
EXPECT_EQ("+42", fmt::format("{0:+}", __int128_t(42)));
#endif
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ull), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("+42", fmt::format("{0:+}", 42.0)); EXPECT_EQ("+42", fmt::format("{0:+}", 42.0));
EXPECT_EQ("+42", fmt::format("{0:+}", 42.0l)); EXPECT_EQ("+42", fmt::format("{0:+}", 42.0l));
EXPECT_THROW_MSG(fmt::format(runtime("{0:+"), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+"), 'c'), format_error,
"missing '}' in format string"); "missing '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 'c'), format_error,
"invalid format specifier for char"); "invalid format specifier for char");
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), "abc"), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), "abc"), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), reinterpret_cast<void*>(0x42)), EXPECT_THROW_MSG(
format_error, "format specifier requires numeric argument"); (void)fmt::format(runtime("{0:+}"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
} }
TEST(format_test, minus_sign) { TEST(format_test, minus_sign) {
EXPECT_EQ("42", fmt::format("{0:-}", 42)); EXPECT_EQ("42", fmt::format("{0:-}", 42));
EXPECT_EQ("-42", fmt::format("{0:-}", -42)); EXPECT_EQ("-42", fmt::format("{0:-}", -42));
EXPECT_EQ("42", fmt::format("{0:-}", 42)); EXPECT_EQ("42", fmt::format("{0:-}", 42));
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 42u), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42u), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("42", fmt::format("{0:-}", 42l)); EXPECT_EQ("42", fmt::format("{0:-}", 42l));
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 42ul), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ul), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("42", fmt::format("{0:-}", 42ll)); EXPECT_EQ("42", fmt::format("{0:-}", 42ll));
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 42ull), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ull), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("42", fmt::format("{0:-}", 42.0)); EXPECT_EQ("42", fmt::format("{0:-}", 42.0));
EXPECT_EQ("42", fmt::format("{0:-}", 42.0l)); EXPECT_EQ("42", fmt::format("{0:-}", 42.0l));
EXPECT_THROW_MSG(fmt::format(runtime("{0:-"), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-"), 'c'), format_error,
"missing '}' in format string"); "missing '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 'c'), format_error,
"invalid format specifier for char"); "invalid format specifier for char");
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), "abc"), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), "abc"), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), reinterpret_cast<void*>(0x42)), EXPECT_THROW_MSG(
format_error, "format specifier requires numeric argument"); (void)fmt::format(runtime("{0:-}"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
} }
TEST(format_test, space_sign) { TEST(format_test, space_sign) {
EXPECT_EQ(" 42", fmt::format("{0: }", 42)); EXPECT_EQ(" 42", fmt::format("{0: }", 42));
EXPECT_EQ("-42", fmt::format("{0: }", -42)); EXPECT_EQ("-42", fmt::format("{0: }", -42));
EXPECT_EQ(" 42", fmt::format("{0: }", 42)); EXPECT_EQ(" 42", fmt::format("{0: }", 42));
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 42u), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42u), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ(" 42", fmt::format("{0: }", 42l)); EXPECT_EQ(" 42", fmt::format("{0: }", 42l));
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 42ul), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ul), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ(" 42", fmt::format("{0: }", 42ll)); EXPECT_EQ(" 42", fmt::format("{0: }", 42ll));
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 42ull), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ull), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ(" 42", fmt::format("{0: }", 42.0)); EXPECT_EQ(" 42", fmt::format("{0: }", 42.0));
EXPECT_EQ(" 42", fmt::format("{0: }", 42.0l)); EXPECT_EQ(" 42", fmt::format("{0: }", 42.0l));
EXPECT_THROW_MSG(fmt::format(runtime("{0: "), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0: "), 'c'), format_error,
"missing '}' in format string"); "missing '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 'c'), format_error,
"invalid format specifier for char"); "invalid format specifier for char");
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), "abc"), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), "abc"), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), reinterpret_cast<void*>(0x42)), EXPECT_THROW_MSG(
format_error, "format specifier requires numeric argument"); (void)fmt::format(runtime("{0: }"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
} }
TEST(format_test, hash_flag) { TEST(format_test, hash_flag) {
@ -670,14 +748,15 @@ TEST(format_test, hash_flag) {
EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.01)); EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.01));
EXPECT_EQ("0.50", fmt::format("{:#.2g}", 0.5)); EXPECT_EQ("0.50", fmt::format("{:#.2g}", 0.5));
EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.5)); EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.5));
EXPECT_THROW_MSG(fmt::format(runtime("{0:#"), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#"), 'c'), format_error,
"missing '}' in format string"); "missing '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), 'c'), format_error,
"invalid format specifier for char"); "invalid format specifier for char");
EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), "abc"), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), "abc"), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), reinterpret_cast<void*>(0x42)), EXPECT_THROW_MSG(
format_error, "format specifier requires numeric argument"); (void)fmt::format(runtime("{0:#}"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument");
} }
TEST(format_test, zero_flag) { TEST(format_test, zero_flag) {
@ -690,14 +769,14 @@ TEST(format_test, zero_flag) {
EXPECT_EQ("00042", fmt::format("{0:05}", 42ull)); EXPECT_EQ("00042", fmt::format("{0:05}", 42ull));
EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0)); EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0));
EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0l)); EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0l));
EXPECT_THROW_MSG(fmt::format(runtime("{0:0"), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:0"), 'c'), format_error,
"missing '}' in format string"); "missing '}' in format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:05}"), 'c'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), 'c'), format_error,
"invalid format specifier for char"); "invalid format specifier for char");
EXPECT_THROW_MSG(fmt::format(runtime("{0:05}"), "abc"), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), "abc"), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
fmt::format(runtime("{0:05}"), reinterpret_cast<void*>(0x42)), (void)fmt::format(runtime("{0:05}"), reinterpret_cast<void*>(0x42)),
format_error, "format specifier requires numeric argument"); format_error, "format specifier requires numeric argument");
} }
@ -705,19 +784,19 @@ TEST(format_test, width) {
char format_str[buffer_size]; char format_str[buffer_size];
safe_sprintf(format_str, "{0:%u", UINT_MAX); safe_sprintf(format_str, "{0:%u", UINT_MAX);
increment(format_str + 3); increment(format_str + 3);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big"); "number is too big");
size_t size = std::strlen(format_str); size_t size = std::strlen(format_str);
format_str[size] = '}'; format_str[size] = '}';
format_str[size + 1] = 0; format_str[size + 1] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big"); "number is too big");
safe_sprintf(format_str, "{0:%u", INT_MAX + 1u); safe_sprintf(format_str, "{0:%u", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big"); "number is too big");
safe_sprintf(format_str, "{0:%u}", INT_MAX + 1u); safe_sprintf(format_str, "{0:%u}", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big"); "number is too big");
EXPECT_EQ(" -42", fmt::format("{0:4}", -42)); EXPECT_EQ(" -42", fmt::format("{0:4}", -42));
EXPECT_EQ(" 42", fmt::format("{0:5}", 42u)); EXPECT_EQ(" 42", fmt::format("{0:5}", 42u));
@ -742,47 +821,47 @@ TEST(format_test, runtime_width) {
char format_str[buffer_size]; char format_str[buffer_size];
safe_sprintf(format_str, "{0:{%u", UINT_MAX); safe_sprintf(format_str, "{0:{%u", UINT_MAX);
increment(format_str + 4); increment(format_str + 4);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"invalid format string"); "invalid format string");
size_t size = std::strlen(format_str); size_t size = std::strlen(format_str);
format_str[size] = '}'; format_str[size] = '}';
format_str[size + 1] = 0; format_str[size + 1] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"argument not found"); "argument not found");
format_str[size + 1] = '}'; format_str[size + 1] = '}';
format_str[size + 2] = 0; format_str[size + 2] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"argument not found"); "argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{"), 0), format_error,
"invalid format string"); "invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{}"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{}"), 0), format_error,
"cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{?}}"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{?}}"), 0), format_error,
"invalid format string"); "invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0), format_error,
"argument not found"); "argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{0:}}"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{0:}}"), 0), format_error,
"invalid format string"); "invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, -1), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1), format_error,
"negative width"); "negative width");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1u)), EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1u)),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, -1l), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, -1l), format_error,
"negative width"); "negative width");
if (fmt::detail::const_check(sizeof(long) > sizeof(int))) { if (fmt::detail::const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX; long value = INT_MAX;
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, (value + 1)), EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (value + 1)),
format_error, "number is too big"); format_error, "number is too big");
} }
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1ul)), EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, (INT_MAX + 1ul)),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, '0'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, '0'), format_error,
"width is not integer"); "width is not integer");
EXPECT_THROW_MSG(fmt::format(runtime("{0:{1}}"), 0, 0.0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, 0.0), format_error,
"width is not integer"); "width is not integer");
EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42, 4)); EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42, 4));
@ -803,53 +882,53 @@ TEST(format_test, precision) {
char format_str[buffer_size]; char format_str[buffer_size];
safe_sprintf(format_str, "{0:.%u", UINT_MAX); safe_sprintf(format_str, "{0:.%u", UINT_MAX);
increment(format_str + 4); increment(format_str + 4);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big"); "number is too big");
size_t size = std::strlen(format_str); size_t size = std::strlen(format_str);
format_str[size] = '}'; format_str[size] = '}';
format_str[size + 1] = 0; format_str[size + 1] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big"); "number is too big");
safe_sprintf(format_str, "{0:.%u", INT_MAX + 1u); safe_sprintf(format_str, "{0:.%u", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big"); "number is too big");
safe_sprintf(format_str, "{0:.%u}", INT_MAX + 1u); safe_sprintf(format_str, "{0:.%u}", INT_MAX + 1u);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"number is too big"); "number is too big");
EXPECT_THROW_MSG(fmt::format(runtime("{0:."), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:."), 0), format_error,
"missing precision specifier"); "missing precision specifier");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.}"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.}"), 0), format_error,
"missing precision specifier"); "missing precision specifier");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2"), 0), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42u), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42u), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42u), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42u), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42l), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42l), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42l), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42l), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42ul), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42ul), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42ul), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42ul), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42ll), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42ll), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42ll), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42ll), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2}"), 42ull), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2}"), 42ull), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.2f}"), 42ull), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.2f}"), 42ull), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:3.0}"), 'x'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:3.0}"), 'x'), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345)); EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345));
EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345l)); EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345l));
@ -866,6 +945,23 @@ TEST(format_test, precision) {
"117102665855668676818703956031062493194527159149245532930545654440112748" "117102665855668676818703956031062493194527159149245532930545654440112748"
"012970999954193198940908041656332452475714786901472678015935523861155013" "012970999954193198940908041656332452475714786901472678015935523861155013"
"480352649347201937902681071074917033322268447533357208324319361e-324"); "480352649347201937902681071074917033322268447533357208324319361e-324");
EXPECT_EQ(
fmt::format("{:.1074f}", 1.1125369292536e-308),
"0.0000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000111253692925360019747947051741965785554081512200979"
"355021686109411883779182127659725163430929750364498219730822952552570601"
"152163505899912777129583674906301179059298598412303893909188340988729019"
"014361467448914817838555156840459458527907308695109202499990850735085304"
"478476991912072201449236975063640913461919914396877093174125167509869762"
"482369631100360266123742648159508919592746619553246586039571522788247697"
"156360766271842991667238355464496455107749716934387136380536472531224398"
"559833794807213172371254492216255558078524900147957309382830827524104234"
"530961756787819847850302379672357738807808384667004752163416921762619527"
"462847642037420991432005657440259928195996762610375541867198059294212446"
"81962777939941034720757232455434770912461317493580281734466552734375");
std::string outputs[] = { std::string outputs[] = {
"-0X1.41FE3FFE71C9E000000000000000000000000000000000000000000000000000000" "-0X1.41FE3FFE71C9E000000000000000000000000000000000000000000000000000000"
@ -895,6 +991,11 @@ TEST(format_test, precision) {
EXPECT_THAT(outputs, EXPECT_THAT(outputs,
testing::Contains(fmt::format("{:.838A}", -2.14001164E+38))); testing::Contains(fmt::format("{:.838A}", -2.14001164E+38)));
if (std::numeric_limits<long double>::digits == 64) {
auto ld = (std::numeric_limits<long double>::min)();
EXPECT_EQ(fmt::format("{:.0}", ld), "3e-4932");
}
EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0)); EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0));
EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234)); EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234));
EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001)); EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001));
@ -903,13 +1004,16 @@ TEST(format_test, precision) {
EXPECT_EQ("1.0e-34", fmt::format("{:.1e}", 1e-34)); EXPECT_EQ("1.0e-34", fmt::format("{:.1e}", 1e-34));
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
fmt::format(runtime("{0:.2}"), reinterpret_cast<void*>(0xcafe)), (void)fmt::format(runtime("{0:.2}"), reinterpret_cast<void*>(0xcafe)),
format_error, "precision not allowed for this argument type"); format_error, "precision not allowed for this argument type");
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
fmt::format(runtime("{0:.2f}"), reinterpret_cast<void*>(0xcafe)), (void)fmt::format(runtime("{0:.2f}"), reinterpret_cast<void*>(0xcafe)),
format_error, "precision not allowed for this argument type"); format_error, "precision not allowed for this argument type");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:.{}e}"), 42.0,
fmt::detail::max_value<int>()),
format_error, "number is too big");
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
fmt::format(runtime("{:.{}e}"), 42.0, fmt::detail::max_value<int>()), (void)fmt::format("{:.2147483646f}", -2.2121295195081227E+304),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_EQ("st", fmt::format("{0:.2}", "str")); EXPECT_EQ("st", fmt::format("{0:.2}", "str"));
@ -919,86 +1023,97 @@ TEST(format_test, runtime_precision) {
char format_str[buffer_size]; char format_str[buffer_size];
safe_sprintf(format_str, "{0:.{%u", UINT_MAX); safe_sprintf(format_str, "{0:.{%u", UINT_MAX);
increment(format_str + 5); increment(format_str + 5);
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"invalid format string"); "invalid format string");
size_t size = std::strlen(format_str); size_t size = std::strlen(format_str);
format_str[size] = '}'; format_str[size] = '}';
format_str[size + 1] = 0; format_str[size + 1] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"argument not found"); "argument not found");
format_str[size + 1] = '}'; format_str[size + 1] = '}';
format_str[size + 2] = 0; format_str[size + 2] = 0;
EXPECT_THROW_MSG(fmt::format(runtime(format_str), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
"argument not found"); "argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{"), 0), format_error,
"invalid format string"); "invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{}"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{}"), 0), format_error,
"cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{?}}"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{?}}"), 0), format_error,
"invalid format string"); "invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}"), 0, 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}"), 0, 0), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0), format_error,
"argument not found"); "argument not found");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{0:}}"), 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{0:}}"), 0), format_error,
"invalid format string"); "invalid format string");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, -1), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, -1), format_error,
"negative precision"); "negative precision");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1u)), EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1u)),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, -1l), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, -1l), format_error,
"negative precision"); "negative precision");
if (fmt::detail::const_check(sizeof(long) > sizeof(int))) { if (fmt::detail::const_check(sizeof(long) > sizeof(int))) {
long value = INT_MAX; long value = INT_MAX;
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, (value + 1)), EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (value + 1)),
format_error, "number is too big"); format_error, "number is too big");
} }
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1ul)), EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, (INT_MAX + 1ul)),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, '0'), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, '0'), format_error,
"precision is not integer"); "precision is not integer");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 0, 0.0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 0, 0.0), format_error,
"precision is not integer"); "precision is not integer");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42, 2), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42, 2), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42u, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42u, 2), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42u, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42u, 2),
format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42l, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42l, 2), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42l, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42l, 2),
format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42ul, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42ul, 2),
format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42ul, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42ul, 2),
format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42ll, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42ll, 2),
format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42ll, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42ll, 2),
format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}}"), 42ull, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"), 42ull, 2),
format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:.{1}f}"), 42ull, 2), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"), 42ull, 2),
format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_THROW_MSG(fmt::format(runtime("{0:3.{1}}"), 'x', 0), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:3.{1}}"), 'x', 0),
format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_EQ("1.2", fmt::format("{0:.{1}}", 1.2345, 2)); EXPECT_EQ("1.2", fmt::format("{0:.{1}}", 1.2345, 2));
EXPECT_EQ("1.2", fmt::format("{1:.{0}}", 2, 1.2345l)); EXPECT_EQ("1.2", fmt::format("{1:.{0}}", 2, 1.2345l));
EXPECT_THROW_MSG( EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"),
fmt::format(runtime("{0:.{1}}"), reinterpret_cast<void*>(0xcafe), 2), reinterpret_cast<void*>(0xcafe), 2),
format_error, "precision not allowed for this argument type"); format_error,
EXPECT_THROW_MSG( "precision not allowed for this argument type");
fmt::format(runtime("{0:.{1}f}"), reinterpret_cast<void*>(0xcafe), 2), EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}f}"),
format_error, "precision not allowed for this argument type"); reinterpret_cast<void*>(0xcafe), 2),
format_error,
"precision not allowed for this argument type");
EXPECT_EQ("st", fmt::format("{0:.{1}}", "str", 2)); EXPECT_EQ("st", fmt::format("{0:.{1}}", "str", 2));
} }
@ -1023,21 +1138,21 @@ TEST(format_test, format_short) {
template <typename T> template <typename T>
void check_unknown_types(const T& value, const char* types, const char*) { void check_unknown_types(const T& value, const char* types, const char*) {
char format_str[buffer_size]; char format_str[buffer_size];
const char* special = ".0123456789L}"; const char* special = ".0123456789L?}";
for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) { for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) {
char c = static_cast<char>(i); char c = static_cast<char>(i);
if (std::strchr(types, c) || std::strchr(special, c) || !c) continue; if (std::strchr(types, c) || std::strchr(special, c) || !c) continue;
safe_sprintf(format_str, "{0:10%c}", c); safe_sprintf(format_str, "{0:10%c}", c);
const char* message = "invalid type specifier"; const char* message = "invalid type specifier";
EXPECT_THROW_MSG(fmt::format(runtime(format_str), value), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), value),
message) format_error, message)
<< format_str << " " << message; << format_str << " " << message;
} }
} }
TEST(format_test, format_int) { TEST(format_test, format_int) {
EXPECT_THROW_MSG(fmt::format(runtime("{0:v"), 42), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:v"), 42), format_error,
"missing '}' in format string"); "invalid type specifier");
check_unknown_types(42, "bBdoxXnLc", "integer"); check_unknown_types(42, "bBdoxXnLc", "integer");
EXPECT_EQ("x", fmt::format("{:c}", static_cast<int>('x'))); EXPECT_EQ("x", fmt::format("{:c}", static_cast<int>('x')));
} }
@ -1192,32 +1307,30 @@ TEST(format_test, format_float) {
} }
TEST(format_test, format_double) { TEST(format_test, format_double) {
EXPECT_EQ("0", fmt::format("{}", 0.0)); EXPECT_EQ(fmt::format("{}", 0.0), "0");
check_unknown_types(1.2, "eEfFgGaAnL%", "double"); check_unknown_types(1.2, "eEfFgGaAnL%", "double");
EXPECT_EQ("0", fmt::format("{:}", 0.0)); EXPECT_EQ(fmt::format("{:}", 0.0), "0");
EXPECT_EQ("0.000000", fmt::format("{:f}", 0.0)); EXPECT_EQ(fmt::format("{:f}", 0.0), "0.000000");
EXPECT_EQ("0", fmt::format("{:g}", 0.0)); EXPECT_EQ(fmt::format("{:g}", 0.0), "0");
EXPECT_EQ("392.65", fmt::format("{:}", 392.65)); EXPECT_EQ(fmt::format("{:}", 392.65), "392.65");
EXPECT_EQ("392.65", fmt::format("{:g}", 392.65)); EXPECT_EQ(fmt::format("{:g}", 392.65), "392.65");
EXPECT_EQ("392.65", fmt::format("{:G}", 392.65)); EXPECT_EQ(fmt::format("{:G}", 392.65), "392.65");
EXPECT_EQ("4.9014e+06", fmt::format("{:g}", 4.9014e6)); EXPECT_EQ(fmt::format("{:g}", 4.9014e6), "4.9014e+06");
EXPECT_EQ("392.650000", fmt::format("{:f}", 392.65)); EXPECT_EQ(fmt::format("{:f}", 392.65), "392.650000");
EXPECT_EQ("392.650000", fmt::format("{:F}", 392.65)); EXPECT_EQ(fmt::format("{:F}", 392.65), "392.650000");
EXPECT_EQ("42", fmt::format("{:L}", 42.0)); EXPECT_EQ(fmt::format("{:L}", 42.0), "42");
EXPECT_EQ(" 0x1.0cccccccccccdp+2", fmt::format("{:24a}", 4.2)); EXPECT_EQ(fmt::format("{:24a}", 4.2), " 0x1.0cccccccccccdp+2");
EXPECT_EQ("0x1.0cccccccccccdp+2 ", fmt::format("{:<24a}", 4.2)); EXPECT_EQ(fmt::format("{:<24a}", 4.2), "0x1.0cccccccccccdp+2 ");
EXPECT_EQ(fmt::format("{0:e}", 392.65), "3.926500e+02");
EXPECT_EQ(fmt::format("{0:E}", 392.65), "3.926500E+02");
EXPECT_EQ(fmt::format("{0:+010.4g}", 392.65), "+0000392.6");
char buffer[buffer_size]; char buffer[buffer_size];
safe_sprintf(buffer, "%e", 392.65);
EXPECT_EQ(buffer, fmt::format("{0:e}", 392.65));
safe_sprintf(buffer, "%E", 392.65);
EXPECT_EQ(buffer, fmt::format("{0:E}", 392.65));
EXPECT_EQ("+0000392.6", fmt::format("{0:+010.4g}", 392.65));
safe_sprintf(buffer, "%a", -42.0); safe_sprintf(buffer, "%a", -42.0);
EXPECT_EQ(buffer, fmt::format("{:a}", -42.0)); EXPECT_EQ(fmt::format("{:a}", -42.0), buffer);
safe_sprintf(buffer, "%A", -42.0); safe_sprintf(buffer, "%A", -42.0);
EXPECT_EQ(buffer, fmt::format("{:A}", -42.0)); EXPECT_EQ(fmt::format("{:A}", -42.0), buffer);
EXPECT_EQ("9223372036854775808.000000", EXPECT_EQ(fmt::format("{:f}", 9223372036854775807.0),
fmt::format("{:f}", 9223372036854775807.0)); "9223372036854775808.000000");
} }
TEST(format_test, precision_rounding) { TEST(format_test, precision_rounding) {
@ -1326,6 +1439,9 @@ TEST(format_test, format_char) {
<< format_str; << format_str;
} }
EXPECT_EQ(fmt::format("{:02X}", n), fmt::format("{:02X}", 'x')); EXPECT_EQ(fmt::format("{:02X}", n), fmt::format("{:02X}", 'x'));
EXPECT_EQ("\n", fmt::format("{}", '\n'));
EXPECT_EQ("'\\n'", fmt::format("{:?}", '\n'));
} }
TEST(format_test, format_volatile_char) { TEST(format_test, format_volatile_char) {
@ -1345,26 +1461,10 @@ TEST(format_test, format_cstring) {
char nonconst[] = "nonconst"; char nonconst[] = "nonconst";
EXPECT_EQ("nonconst", fmt::format("{0}", nonconst)); EXPECT_EQ("nonconst", fmt::format("{0}", nonconst));
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
fmt::format(runtime("{0}"), static_cast<const char*>(nullptr)), (void)fmt::format(runtime("{0}"), static_cast<const char*>(nullptr)),
format_error, "string pointer is null"); format_error, "string pointer is null");
} }
TEST(format_test, format_schar_string) {
signed char str[] = "test";
EXPECT_EQ("test", fmt::format("{0:s}", str));
const signed char* const_str = str;
EXPECT_EQ("test", fmt::format("{0:s}", const_str));
}
TEST(format_test, format_uchar_string) {
unsigned char str[] = "test";
EXPECT_EQ("test", fmt::format("{0:s}", str));
const unsigned char* const_str = str;
EXPECT_EQ("test", fmt::format("{0:s}", const_str));
unsigned char* ptr = str;
EXPECT_EQ("test", fmt::format("{0:s}", ptr));
}
void function_pointer_test(int, double, std::string) {} void function_pointer_test(int, double, std::string) {}
TEST(format_test, format_pointer) { TEST(format_test, format_pointer) {
@ -1388,14 +1488,43 @@ TEST(format_test, format_pointer) {
EXPECT_EQ("0x0", fmt::format("{}", nullptr)); EXPECT_EQ("0x0", fmt::format("{}", nullptr));
} }
TEST(format_test, write_uintptr_fallback) {
// Test that formatting a pointer by converting it to uint128_fallback works.
// This is needed to support systems without uintptr_t.
auto s = std::string();
fmt::detail::write_ptr<char>(
std::back_inserter(s),
fmt::detail::bit_cast<fmt::detail::uint128_fallback>(
reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ(s, "0xface");
}
enum class color { red, green, blue };
namespace test_ns {
enum class color { red, green, blue };
using fmt::enums::format_as;
} // namespace test_ns
TEST(format_test, format_enum_class) {
EXPECT_EQ(fmt::format("{}", fmt::underlying(color::red)), "0");
EXPECT_EQ(fmt::format("{}", test_ns::color::red), "0");
}
TEST(format_test, format_string) { TEST(format_test, format_string) {
EXPECT_EQ("test", fmt::format("{0}", std::string("test"))); EXPECT_EQ(fmt::format("{0}", std::string("test")), "test");
EXPECT_THROW(fmt::format(fmt::runtime("{:x}"), std::string("test")), EXPECT_EQ(fmt::format("{0}", std::string("test")), "test");
EXPECT_EQ(fmt::format("{:?}", std::string("test")), "\"test\"");
EXPECT_EQ(fmt::format("{:*^10?}", std::string("test")), "**\"test\"**");
EXPECT_EQ(fmt::format("{:?}", std::string("\test")), "\"\\test\"");
EXPECT_THROW((void)fmt::format(fmt::runtime("{:x}"), std::string("test")),
fmt::format_error); fmt::format_error);
} }
TEST(format_test, format_string_view) { TEST(format_test, format_string_view) {
EXPECT_EQ("test", fmt::format("{}", string_view("test"))); EXPECT_EQ("test", fmt::format("{}", string_view("test")));
EXPECT_EQ("\"t\\nst\"", fmt::format("{:?}", string_view("t\nst")));
EXPECT_EQ("", fmt::format("{}", string_view())); EXPECT_EQ("", fmt::format("{}", string_view()));
} }
@ -1481,7 +1610,7 @@ template <> struct formatter<Answer> : formatter<int> {
FMT_END_NAMESPACE FMT_END_NAMESPACE
TEST(format_test, format_custom) { TEST(format_test, format_custom) {
EXPECT_THROW_MSG(fmt::format(runtime("{:s}"), date(2012, 12, 9)), EXPECT_THROW_MSG((void)fmt::format(runtime("{:s}"), date(2012, 12, 9)),
format_error, "unknown format specifier"); format_error, "unknown format specifier");
EXPECT_EQ("42", fmt::format("{0}", Answer())); EXPECT_EQ("42", fmt::format("{0}", Answer()));
EXPECT_EQ("0042", fmt::format("{:04}", Answer())); EXPECT_EQ("0042", fmt::format("{:04}", Answer()));
@ -1558,8 +1687,9 @@ TEST(format_test, format_examples) {
fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}", 42)); fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}", 42));
EXPECT_EQ("The answer is 42", fmt::format("The answer is {}", 42)); EXPECT_EQ("The answer is 42", fmt::format("The answer is {}", 42));
EXPECT_THROW_MSG(fmt::format(runtime("The answer is {:d}"), "forty-two"), EXPECT_THROW_MSG(
format_error, "invalid type specifier"); (void)fmt::format(runtime("The answer is {:d}"), "forty-two"),
format_error, "invalid type specifier");
EXPECT_WRITE( EXPECT_WRITE(
stdout, fmt::print("{}", std::numeric_limits<double>::infinity()), "inf"); stdout, fmt::print("{}", std::numeric_limits<double>::infinity()), "inf");
@ -1595,6 +1725,11 @@ TEST(format_test, bytes) {
EXPECT_EQ(10, s.size()); EXPECT_EQ(10, s.size());
} }
TEST(format_test, group_digits_view) {
EXPECT_EQ(fmt::format("{}", fmt::group_digits(10000000)), "10,000,000");
EXPECT_EQ(fmt::format("{:8}", fmt::group_digits(1000)), " 1,000");
}
enum test_enum { foo, bar }; enum test_enum { foo, bar };
TEST(format_test, join) { TEST(format_test, join) {
@ -1718,6 +1853,21 @@ TEST(format_test, custom_format_compile_time_string) {
using namespace fmt::literals; using namespace fmt::literals;
# if FMT_GCC_VERSION
# define FMT_CHECK_DEPRECATED_UDL_FORMAT 1
# elif FMT_CLANG_VERSION && defined(__has_warning)
# if __has_warning("-Wdeprecated-declarations")
# define FMT_CHECK_DEPRECATED_UDL_FORMAT 1
# endif
# endif
# ifndef FMT_CHECK_DEPRECATED_UDL_FORMAT
# define FMT_CHECK_DEPRECATED_UDL_FORMAT 0
# endif
# if FMT_CHECK_DEPRECATED_UDL_FORMAT
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
TEST(format_test, format_udl) { TEST(format_test, format_udl) {
EXPECT_EQ("{}c{}"_format("ab", 1), fmt::format("{}c{}", "ab", 1)); EXPECT_EQ("{}c{}"_format("ab", 1), fmt::format("{}c{}", "ab", 1));
EXPECT_EQ("foo"_format(), "foo"); EXPECT_EQ("foo"_format(), "foo");
@ -1725,6 +1875,9 @@ TEST(format_test, format_udl) {
EXPECT_EQ("{}"_format(date(2015, 10, 21)), "2015-10-21"); EXPECT_EQ("{}"_format(date(2015, 10, 21)), "2015-10-21");
} }
# pragma GCC diagnostic pop
# endif
TEST(format_test, named_arg_udl) { TEST(format_test, named_arg_udl) {
auto udl_a = fmt::format("{first}{second}{first}{third}", "first"_a = "abra", auto udl_a = fmt::format("{first}{second}{first}{third}", "first"_a = "abra",
"second"_a = "cad", "third"_a = 99); "second"_a = "cad", "third"_a = 99);
@ -1777,21 +1930,21 @@ TEST(format_test, dynamic_formatter) {
EXPECT_EQ("42", fmt::format("{:d}", num)); EXPECT_EQ("42", fmt::format("{:d}", num));
EXPECT_EQ("foo", fmt::format("{:s}", str)); EXPECT_EQ("foo", fmt::format("{:s}", str));
EXPECT_EQ(" 42 foo ", fmt::format("{:{}} {:{}}", num, 3, str, 4)); EXPECT_EQ(" 42 foo ", fmt::format("{:{}} {:{}}", num, 3, str, 4));
EXPECT_THROW_MSG(fmt::format(runtime("{0:{}}"), num), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{}}"), num), format_error,
"cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{:{0}}"), num), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:{0}}"), num), format_error,
"cannot switch from automatic to manual argument indexing"); "cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(fmt::format(runtime("{:+}"), str), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:+}"), str), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{:-}"), str), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:-}"), str), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{: }"), str), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{: }"), str), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{:#}"), str), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:#}"), str), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{:0}"), str), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:0}"), str), format_error,
"format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{:.2}"), num), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{:.2}"), num), format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
} }
@ -1815,13 +1968,20 @@ struct formatter<adl_test::fmt::detail::foo> : formatter<std::string> {
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
TEST(format_test, to_string) { struct convertible_to_int {
EXPECT_EQ("42", fmt::to_string(42)); operator int() const { return value; }
EXPECT_EQ("0x1234", fmt::to_string(reinterpret_cast<void*>(0x1234)));
EXPECT_EQ("foo", fmt::to_string(adl_test::fmt::detail::foo()));
enum test_enum2 : unsigned char { test_value }; int value = 42;
EXPECT_EQ("0", fmt::to_string(test_value)); };
TEST(format_test, to_string) {
EXPECT_EQ(fmt::to_string(42), "42");
EXPECT_EQ(fmt::to_string(reinterpret_cast<void*>(0x1234)), "0x1234");
EXPECT_EQ(fmt::to_string(adl_test::fmt::detail::foo()), "foo");
EXPECT_EQ(fmt::to_string(convertible_to_int()), "42");
enum foo : unsigned char { zero };
EXPECT_EQ(fmt::to_string(zero), "0");
} }
TEST(format_test, output_iterators) { TEST(format_test, output_iterators) {

View File

@ -25,6 +25,6 @@ function(add_fuzzer source)
target_compile_features(${name} PRIVATE cxx_generic_lambdas) target_compile_features(${name} PRIVATE cxx_generic_lambdas)
endfunction() endfunction()
foreach (source chrono-duration.cc float.cc named-arg.cc one-arg.cc two-args.cc) foreach (source chrono-duration.cc chrono-timepoint.cc float.cc named-arg.cc one-arg.cc two-args.cc)
add_fuzzer(${source}) add_fuzzer(${source})
endforeach () endforeach ()

View File

@ -22,6 +22,8 @@ here=$(pwd)
CXXFLAGSALL="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g" CXXFLAGSALL="-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION= -g"
CMAKEFLAGSALL="$root -GNinja -DCMAKE_BUILD_TYPE=Debug -DFMT_DOC=Off -DFMT_TEST=Off -DFMT_FUZZ=On -DCMAKE_CXX_STANDARD=17" CMAKEFLAGSALL="$root -GNinja -DCMAKE_BUILD_TYPE=Debug -DFMT_DOC=Off -DFMT_TEST=Off -DFMT_FUZZ=On -DCMAKE_CXX_STANDARD=17"
CLANG=clang++-11
# For performance analysis of the fuzzers. # For performance analysis of the fuzzers.
builddir=$here/build-fuzzers-perfanalysis builddir=$here/build-fuzzers-perfanalysis
mkdir -p $builddir mkdir -p $builddir
@ -37,7 +39,7 @@ cmake --build $builddir
builddir=$here/build-fuzzers-ossfuzz builddir=$here/build-fuzzers-ossfuzz
mkdir -p $builddir mkdir -p $builddir
cd $builddir cd $builddir
CXX="clang++" \ CXX=$CLANG \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link" cmake \ CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link" cmake \
cmake $CMAKEFLAGSALL \ cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \ -DFMT_FUZZ_LINKMAIN=Off \
@ -50,7 +52,7 @@ cmake --build $builddir
builddir=$here/build-fuzzers-libfuzzer builddir=$here/build-fuzzers-libfuzzer
mkdir -p $builddir mkdir -p $builddir
cd $builddir cd $builddir
CXX="clang++" \ CXX=$CLANG \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,address,undefined" cmake \ CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link,address,undefined" cmake \
cmake $CMAKEFLAGSALL \ cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \ -DFMT_FUZZ_LINKMAIN=Off \
@ -62,7 +64,7 @@ cmake --build $builddir
builddir=$here/build-fuzzers-fast builddir=$here/build-fuzzers-fast
mkdir -p $builddir mkdir -p $builddir
cd $builddir cd $builddir
CXX="clang++" \ CXX=$CLANG \
CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link -O3" cmake \ CXXFLAGS="$CXXFLAGSALL -fsanitize=fuzzer-no-link -O3" cmake \
cmake $CMAKEFLAGSALL \ cmake $CMAKEFLAGSALL \
-DFMT_FUZZ_LINKMAIN=Off \ -DFMT_FUZZ_LINKMAIN=Off \

View File

@ -1,9 +1,10 @@
// Copyright (c) 2019, Paul Dreik // Copyright (c) 2019, Paul Dreik
// For the license information refer to format.h. // For the license information refer to format.h.
#include <cstdint>
#include <fmt/chrono.h> #include <fmt/chrono.h>
#include <cstdint>
#include "fuzzer-common.h" #include "fuzzer-common.h"
template <typename Period, typename Rep> template <typename Period, typename Rep>
@ -31,7 +32,7 @@ void invoke_outer(const uint8_t* data, size_t size, int period) {
data += fixed_size; data += fixed_size;
size -= fixed_size; size -= fixed_size;
// data is already allocated separately in libFuzzer so reading past the end // data is already allocated separately in libFuzzer so reading past the end
// will most likely be detected anyway. // will most likely be detected anyway.
const auto format_str = fmt::string_view(as_chars(data), size); const auto format_str = fmt::string_view(as_chars(data), size);
@ -86,7 +87,7 @@ void invoke_outer(const uint8_t* data, size_t size, int period) {
} }
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size <= 4) return 0; if (size <= 4) return 0;
const auto representation = data[0]; const auto representation = data[0];
const auto period = data[1]; const auto period = data[1];

View File

@ -0,0 +1,32 @@
// Copyright (c) 2021, Paul Dreik
// For license information refer to format.h.
#include <fmt/chrono.h>
#include "fuzzer-common.h"
/*
* a fuzzer for the chrono timepoints formatters
* C is a clock (std::chrono::system_clock etc)
*/
template <typename C> void doit(const uint8_t* data, size_t size) {
using Rep = typename C::time_point::rep;
constexpr auto N = sizeof(Rep);
if (size < N) return;
const auto x = assign_from_buf<Rep>(data);
typename C::duration dur{x};
typename C::time_point timepoint{dur};
data += N;
size -= N;
data_to_string format_str(data, size);
std::string message = fmt::format(format_str.get(), timepoint);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
try {
doit<std::chrono::system_clock>(data, size);
} catch (...) {
}
return 0;
}

View File

@ -1,17 +1,18 @@
// A fuzzer for floating-point formatter. // A fuzzer for floating-point formatter.
// For the license information refer to format.h. // For the license information refer to format.h.
#include <fmt/format.h>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <stdexcept>
#include <limits> #include <limits>
#include <fmt/format.h> #include <stdexcept>
#include "fuzzer-common.h" #include "fuzzer-common.h"
void check_round_trip(fmt::string_view format_str, double value) { void check_round_trip(fmt::string_view format_str, double value) {
auto buffer = fmt::memory_buffer(); auto buffer = fmt::memory_buffer();
fmt::format_to(buffer, format_str, value); fmt::format_to(std::back_inserter(buffer), format_str, value);
if (std::isnan(value)) { if (std::isnan(value)) {
auto nan = std::signbit(value) ? "-nan" : "nan"; auto nan = std::signbit(value) ? "-nan" : "nan";
@ -24,8 +25,7 @@ void check_round_trip(fmt::string_view format_str, double value) {
char* ptr = nullptr; char* ptr = nullptr;
if (std::strtod(buffer.data(), &ptr) != value) if (std::strtod(buffer.data(), &ptr) != value)
throw std::runtime_error("round trip failure"); throw std::runtime_error("round trip failure");
if (ptr + 1 != buffer.end()) if (ptr + 1 != buffer.end()) throw std::runtime_error("unparsed output");
throw std::runtime_error("unparsed output");
} }
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {

View File

@ -4,12 +4,12 @@
#ifndef FUZZER_COMMON_H #ifndef FUZZER_COMMON_H
#define FUZZER_COMMON_H #define FUZZER_COMMON_H
#include <cstdint> // std::uint8_t
#include <cstring> // memcpy
#include <vector>
#include <fmt/core.h> #include <fmt/core.h>
#include <cstdint> // std::uint8_t
#include <cstring> // memcpy
#include <vector>
// One can format to either a string, or a buffer. The latter is faster, but // One can format to either a string, or a buffer. The latter is faster, but
// one may be interested in formatting to a string instead to verify it works // one may be interested in formatting to a string instead to verify it works
// as intended. To avoid a combinatoric explosion, select this at compile time // as intended. To avoid a combinatoric explosion, select this at compile time
@ -56,7 +56,9 @@ struct data_to_string {
data_to_string(const uint8_t* data, size_t size, bool add_terminator = false) data_to_string(const uint8_t* data, size_t size, bool add_terminator = false)
: buffer(size + (add_terminator ? 1 : 0)) { : buffer(size + (add_terminator ? 1 : 0)) {
std::memcpy(buffer.data(), data, size); if (size) {
std::memcpy(buffer.data(), data, size);
}
} }
fmt::string_view get() const { return {buffer.data(), buffer.size()}; } fmt::string_view get() const { return {buffer.data(), buffer.size()}; }

View File

@ -1,10 +1,11 @@
// Copyright (c) 2019, Paul Dreik // Copyright (c) 2019, Paul Dreik
// For the license information refer to format.h. // For the license information refer to format.h.
#include <fmt/chrono.h>
#include <cstdint> #include <cstdint>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
#include <fmt/chrono.h>
#include "fuzzer-common.h" #include "fuzzer-common.h"
@ -25,10 +26,11 @@ void invoke_fmt(const uint8_t* data, size_t size, unsigned arg_name_size) {
try { try {
#if FMT_FUZZ_FORMAT_TO_STRING #if FMT_FUZZ_FORMAT_TO_STRING
std::string message = std::string message =
fmt::format(format_str.get(), fmt::arg(arg_name.data(), value)); fmt::format(format_str.get(), fmt::arg(arg_name.data(), value));
#else #else
fmt::memory_buffer out; fmt::memory_buffer out;
fmt::format_to(out, format_str.get(), fmt::arg(arg_name.data(), value)); fmt::format_to(std::back_inserter(out), format_str.get(),
fmt::arg(arg_name.data(), value));
#endif #endif
} catch (std::exception&) { } catch (std::exception&) {
} }

View File

@ -1,17 +1,18 @@
// Copyright (c) 2019, Paul Dreik // Copyright (c) 2019, Paul Dreik
// For the license information refer to format.h. // For the license information refer to format.h.
#include <fmt/chrono.h>
#include <cstdint> #include <cstdint>
#include <exception> #include <exception>
#include <fmt/chrono.h>
#include "fuzzer-common.h" #include "fuzzer-common.h"
template <typename T, typename Repr> template <typename T, typename Repr> const T* from_repr(const Repr& r) {
const T* from_repr(const Repr& r) { return &r; } return &r;
}
template <> template <> const std::tm* from_repr<std::tm>(const std::time_t& t) {
const std::tm* from_repr<std::tm>(const std::time_t& t) {
return std::localtime(&t); return std::localtime(&t);
} }

View File

@ -1,10 +1,11 @@
// Copyright (c) 2019, Paul Dreik // Copyright (c) 2019, Paul Dreik
// For the license information refer to format.h. // For the license information refer to format.h.
#include <fmt/format.h>
#include <cstdint> #include <cstdint>
#include <exception> #include <exception>
#include <string> #include <string>
#include <fmt/format.h>
#include "fuzzer-common.h" #include "fuzzer-common.h"

View File

@ -23,7 +23,7 @@ output_redirect::output_redirect(FILE* f) : file_(f) {
write_end.dup2(fd); write_end.dup2(fd);
} }
output_redirect::~output_redirect() FMT_NOEXCEPT { output_redirect::~output_redirect() noexcept {
try { try {
restore(); restore();
} catch (const std::exception& e) { } catch (const std::exception& e) {

View File

@ -15,8 +15,8 @@
#ifdef FMT_MODULE_TEST #ifdef FMT_MODULE_TEST
import fmt; import fmt;
#else #else
#include "fmt/os.h" # include "fmt/os.h"
#endif // FMG_MODULE_TEST #endif // FMG_MODULE_TEST
#include "gmock/gmock.h" #include "gmock/gmock.h"
@ -83,7 +83,7 @@ class output_redirect {
public: public:
explicit output_redirect(FILE* file); explicit output_redirect(FILE* file);
~output_redirect() FMT_NOEXCEPT; ~output_redirect() noexcept;
output_redirect(const output_redirect&) = delete; output_redirect(const output_redirect&) = delete;
void operator=(const output_redirect&) = delete; void operator=(const output_redirect&) = delete;

View File

@ -17,6 +17,13 @@ else ()
target_compile_definitions(gtest PUBLIC GTEST_HAS_PTHREAD=0) target_compile_definitions(gtest PUBLIC GTEST_HAS_PTHREAD=0)
endif () endif ()
# Workaround GTest bug https://github.com/google/googletest/issues/705.
check_cxx_compiler_flag(
-fno-delete-null-pointer-checks HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
if (HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
target_compile_options(gtest PUBLIC -fno-delete-null-pointer-checks)
endif ()
if (MSVC) if (MSVC)
# Disable MSVC warnings of _CRT_INSECURE_DEPRECATE functions. # Disable MSVC warnings of _CRT_INSECURE_DEPRECATE functions.
target_compile_definitions(gtest PRIVATE _CRT_SECURE_NO_WARNINGS) target_compile_definitions(gtest PRIVATE _CRT_SECURE_NO_WARNINGS)

View File

@ -1,7 +1,11 @@
// Header-only configuration test // Header-only configuration test
#include "fmt/core.h" #include "fmt/core.h"
#include "fmt/ostream.h"
#include "gtest/gtest.h"
#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
# error "Not in the header-only mode." # error "Not in the header-only mode."
#endif #endif
TEST(header_only_test, format) { EXPECT_EQ(fmt::format("foo"), "foo"); }

View File

@ -36,7 +36,6 @@
#else #else
# define FMT_USE_FCNTL 0 # define FMT_USE_FCNTL 0
#endif #endif
#define FMT_NOEXCEPT noexcept
#if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)
# define FMT_POSIX(call) _##call # define FMT_POSIX(call) _##call
#else #else
@ -76,8 +75,10 @@ bool oops_detail_namespace_is_visible;
namespace fmt { namespace fmt {
bool namespace_detail_invisible() { bool namespace_detail_invisible() {
#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \ #if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \
_MSC_FULL_VER <= 192930129 ((_MSC_VER == 1929 && _MSC_FULL_VER <= 192930136) || \
// bug in msvc up to 16.11-pre1: (_MSC_VER == 1930 && _MSC_FULL_VER <= 193030704))
// bug in msvc up to 16.11.5 / 17.0-pre5:
// the namespace is visible even when it is neither // the namespace is visible even when it is neither
// implicitly nor explicitly exported // implicitly nor explicitly exported
return true; return true;
@ -97,8 +98,8 @@ TEST(module_test, detail_namespace) {
// macros must not be imported from a *named* module [cpp.import]/5.1 // macros must not be imported from a *named* module [cpp.import]/5.1
TEST(module_test, macros) { TEST(module_test, macros) {
#if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \ #if defined(FMT_HIDE_MODULE_BUGS) && defined(_MSC_FULL_VER) && \
_MSC_FULL_VER <= 192930129 _MSC_FULL_VER <= 192930130
// bug in msvc up to 16.11-pre1: // bug in msvc up to 16.11-pre2:
// include-guard macros leak from BMI // include-guard macros leak from BMI
// and even worse: they cannot be #undef-ined // and even worse: they cannot be #undef-ined
macro_leaked = false; macro_leaked = false;
@ -194,13 +195,6 @@ TEST(module_test, wformat_args) {
EXPECT_TRUE(args.get(0)); EXPECT_TRUE(args.get(0));
} }
TEST(module_test, checked_format_args) {
fmt::basic_format_args args = fmt::make_args_checked<int>("{}", 42);
EXPECT_TRUE(args.get(0));
fmt::basic_format_args wargs = fmt::make_args_checked<int>(L"{}", 42);
EXPECT_TRUE(wargs.get(0));
}
TEST(module_test, dynamic_format_args) { TEST(module_test, dynamic_format_args) {
fmt::dynamic_format_arg_store<fmt::format_context> dyn_store; fmt::dynamic_format_arg_store<fmt::format_context> dyn_store;
dyn_store.push_back(fmt::arg("a42", 42)); dyn_store.push_back(fmt::arg("a42", 42));
@ -456,8 +450,7 @@ TEST(module_test, time_duration) {
} }
TEST(module_test, weekday) { TEST(module_test, weekday) {
EXPECT_EQ("Monday", EXPECT_EQ("Mon", fmt::format(std::locale::classic(), "{}", fmt::weekday(1)));
std::format(std::locale::classic(), "{:%A}", fmt::weekday(1)));
} }
TEST(module_test, to_string_view) { TEST(module_test, to_string_view) {

18
vendor/Fmt/test/noexception-test.cc vendored Normal file
View File

@ -0,0 +1,18 @@
// Formatting library for C++ - Noexception tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/args.h"
#include "fmt/chrono.h"
#include "fmt/color.h"
#include "fmt/compile.h"
#include "fmt/core.h"
#include "fmt/format.h"
#include "fmt/os.h"
#include "fmt/ostream.h"
#include "fmt/printf.h"
#include "fmt/ranges.h"
#include "fmt/xchar.h"

View File

@ -548,13 +548,4 @@ TEST(file_test, fdopen) {
int read_fd = read_end.descriptor(); int read_fd = read_end.descriptor();
EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get()))); EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
} }
# ifdef FMT_LOCALE
TEST(locale_test, strtod) {
fmt::locale loc;
const char *start = "4.2", *ptr = start;
EXPECT_EQ(4.2, loc.strtod(ptr));
EXPECT_EQ(start + 3, ptr);
}
# endif
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL

View File

@ -23,6 +23,7 @@ template <> struct formatter<test> : formatter<int> {
#include <sstream> #include <sstream>
#include "fmt/compile.h"
#include "fmt/ostream.h" #include "fmt/ostream.h"
#include "fmt/ranges.h" #include "fmt/ranges.h"
#include "gmock/gmock.h" #include "gmock/gmock.h"
@ -52,6 +53,16 @@ std::ostream& operator<<(std::ostream& os, streamable_enum) {
enum unstreamable_enum {}; enum unstreamable_enum {};
struct empty_test {};
std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; }
namespace fmt {
template <> struct formatter<test_string> : ostream_formatter {};
template <> struct formatter<date> : ostream_formatter {};
template <> struct formatter<streamable_enum> : ostream_formatter {};
template <> struct formatter<empty_test> : ostream_formatter {};
} // namespace fmt
TEST(ostream_test, enum) { TEST(ostream_test, enum) {
EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum())); EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum()));
EXPECT_EQ("0", fmt::format("{}", unstreamable_enum())); EXPECT_EQ("0", fmt::format("{}", unstreamable_enum()));
@ -69,25 +80,22 @@ TEST(ostream_test, format_specs) {
EXPECT_EQ(" def", fmt::format("{0:>5}", test_string("def"))); EXPECT_EQ(" def", fmt::format("{0:>5}", test_string("def")));
EXPECT_EQ(" def ", fmt::format("{0:^5}", test_string("def"))); EXPECT_EQ(" def ", fmt::format("{0:^5}", test_string("def")));
EXPECT_EQ("def**", fmt::format("{0:*<5}", test_string("def"))); EXPECT_EQ("def**", fmt::format("{0:*<5}", test_string("def")));
EXPECT_THROW_MSG(fmt::format(runtime("{0:+}"), test_string()), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), test_string()),
"format specifier requires numeric argument"); format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:-}"), test_string()), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), test_string()),
"format specifier requires numeric argument"); format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0: }"), test_string()), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), test_string()),
"format specifier requires numeric argument"); format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:#}"), test_string()), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), test_string()),
"format specifier requires numeric argument"); format_error, "format specifier requires numeric argument");
EXPECT_THROW_MSG(fmt::format(runtime("{0:05}"), test_string()), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), test_string()),
"format specifier requires numeric argument"); format_error, "format specifier requires numeric argument");
EXPECT_EQ("test ", fmt::format("{0:13}", test_string("test"))); EXPECT_EQ("test ", fmt::format("{0:13}", test_string("test")));
EXPECT_EQ("test ", fmt::format("{0:{1}}", test_string("test"), 13)); EXPECT_EQ("test ", fmt::format("{0:{1}}", test_string("test"), 13));
EXPECT_EQ("te", fmt::format("{0:.2}", test_string("test"))); EXPECT_EQ("te", fmt::format("{0:.2}", test_string("test")));
EXPECT_EQ("te", fmt::format("{0:.{1}}", test_string("test"), 2)); EXPECT_EQ("te", fmt::format("{0:.{1}}", test_string("test"), 2));
} }
struct empty_test {};
std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; }
TEST(ostream_test, empty_custom_output) { TEST(ostream_test, empty_custom_output) {
EXPECT_EQ("", fmt::format("{}", empty_test())); EXPECT_EQ("", fmt::format("{}", empty_test()));
} }
@ -183,6 +191,8 @@ template <typename T> struct formatter<test_template<T>> : formatter<int> {
return formatter<int>::format(2, ctx); return formatter<int>::format(2, ctx);
} }
}; };
template <> struct formatter<fmt_test::abc> : ostream_formatter {};
} // namespace fmt } // namespace fmt
TEST(ostream_test, template) { TEST(ostream_test, template) {
@ -213,41 +223,6 @@ TEST(ostream_test, disable_builtin_ostream_operators) {
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo"))); EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
} }
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_string_like) {
return os << "bar";
}
TEST(ostream_test, format_explicitly_convertible_to_string_like) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator fmt::detail::std_string_view<char>() const {
return {"foo", 3u};
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_std_string_view) {
return os << "bar";
}
TEST(ostream_test, format_explicitly_convertible_to_std_string_view) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#endif // FMT_USE_STRING_VIEW
struct streamable_and_convertible_to_bool { struct streamable_and_convertible_to_bool {
operator bool() const { return true; } operator bool() const { return true; }
}; };
@ -257,7 +232,23 @@ std::ostream& operator<<(std::ostream& os, streamable_and_convertible_to_bool) {
} }
TEST(ostream_test, format_convertible_to_bool) { TEST(ostream_test, format_convertible_to_bool) {
EXPECT_EQ("foo", fmt::format("{}", streamable_and_convertible_to_bool())); // operator<< is intentionally not used because of potential ODR violations.
EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_bool()), "true");
}
struct streamable_and_convertible_to_string_view {
operator fmt::string_view() const { return "foo"; }
};
std::ostream& operator<<(std::ostream& os,
streamable_and_convertible_to_string_view) {
return os << "bar";
}
TEST(ostream_test, format_convertible_to_string_vew) {
// operator<< is intentionally not used because of potential ODR violations.
EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_string_view()),
"foo");
} }
struct copyfmt_test {}; struct copyfmt_test {};
@ -268,6 +259,10 @@ std::ostream& operator<<(std::ostream& os, copyfmt_test) {
return os << "foo"; return os << "foo";
} }
namespace fmt {
template <> struct formatter<copyfmt_test> : ostream_formatter {};
} // namespace fmt
TEST(ostream_test, copyfmt) { TEST(ostream_test, copyfmt) {
EXPECT_EQ("foo", fmt::format("{}", copyfmt_test())); EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
} }
@ -280,3 +275,24 @@ TEST(ostream_test, range) {
auto strs = std::vector<test_string>{test_string("foo"), test_string("bar")}; auto strs = std::vector<test_string>{test_string("foo"), test_string("bar")};
EXPECT_EQ("[foo, bar]", fmt::format("{}", strs)); EXPECT_EQ("[foo, bar]", fmt::format("{}", strs));
} }
struct abstract {
virtual ~abstract() = default;
virtual void f() = 0;
friend std::ostream& operator<<(std::ostream& os, const abstract&) {
return os;
}
};
namespace fmt {
template <> struct formatter<abstract> : ostream_formatter {};
} // namespace fmt
void format_abstract_compiles(const abstract& a) {
fmt::format(FMT_COMPILE("{}"), a);
}
TEST(ostream_test, is_formattable) {
EXPECT_TRUE(fmt::is_formattable<std::string>());
EXPECT_TRUE(fmt::is_formattable<fmt::detail::std_string_view<char>>());
}

View File

@ -457,110 +457,3 @@ TEST(scoped_mock, scope) {
} }
EXPECT_EQ(nullptr, test_mock::instance); EXPECT_EQ(nullptr, test_mock::instance);
} }
#ifdef FMT_LOCALE
using locale_type = fmt::locale::type;
struct locale_mock {
static locale_mock* instance;
MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale,
locale_type base));
MOCK_METHOD1(freelocale, void(locale_type locale));
MOCK_METHOD3(strtod_l,
double(const char* nptr, char** endptr, locale_type locale));
} * locale_mock::instance;
# ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4273)
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Winconsistent-dllimport"
# endif
_locale_t _create_locale(int category, const char* locale) {
return locale_mock::instance->newlocale(category, locale, 0);
}
void _free_locale(_locale_t locale) {
locale_mock::instance->freelocale(locale);
}
double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return locale_mock::instance->strtod_l(nptr, endptr, locale);
}
# ifdef __clang__
# pragma clang diagnostic pop
# endif
# pragma warning(pop)
# endif
# if defined(__THROW) && \
((FMT_GCC_VERSION > 0 && FMT_GCC_VERSION <= 408) || defined(__e2k__))
# define FMT_LOCALE_THROW __THROW
# else
# define FMT_LOCALE_THROW
# endif
# if defined(__APPLE__) || \
(defined(__FreeBSD__) && __FreeBSD_version < 1200002)
typedef int FreeLocaleResult;
# else
typedef void FreeLocaleResult;
# endif
FreeLocaleResult freelocale(locale_type locale) FMT_LOCALE_THROW {
locale_mock::instance->freelocale(locale);
return FreeLocaleResult();
}
double strtod_l(const char* nptr, char** endptr,
locale_type locale) FMT_LOCALE_THROW {
return locale_mock::instance->strtod_l(nptr, endptr, locale);
}
# undef FMT_LOCALE_THROW
# ifndef _WIN32
locale_t test::newlocale(int category_mask, const char* locale, locale_t base) {
return locale_mock::instance->newlocale(category_mask, locale, base);
}
TEST(locale_test, locale_mock) {
scoped_mock<locale_mock> mock;
auto locale = reinterpret_cast<locale_type>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
FMT_SYSTEM(newlocale(222, "foo", locale));
}
# endif
TEST(locale_test, locale) {
# ifndef LC_NUMERIC_MASK
enum { LC_NUMERIC_MASK = LC_NUMERIC };
# endif
scoped_mock<locale_mock> mock;
auto impl = reinterpret_cast<locale_type>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
.WillOnce(Return(impl));
EXPECT_CALL(mock, freelocale(impl));
fmt::locale loc;
EXPECT_EQ(impl, loc.get());
}
TEST(locale_test, strtod) {
scoped_mock<locale_mock> mock;
EXPECT_CALL(mock, newlocale(_, _, _))
.WillOnce(Return(reinterpret_cast<locale_type>(42)));
EXPECT_CALL(mock, freelocale(_));
fmt::locale loc;
const char* str = "4.2";
char end = 'x';
EXPECT_CALL(mock, strtod_l(str, _, loc.get()))
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));
EXPECT_EQ(777, loc.strtod(str));
EXPECT_EQ(&end, str);
}
#endif // FMT_LOCALE

View File

@ -11,7 +11,6 @@
#include <climits> #include <climits>
#include <cstring> #include <cstring>
#include "fmt/ostream.h"
#include "fmt/xchar.h" #include "fmt/xchar.h"
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.h" #include "util.h"
@ -481,12 +480,6 @@ TEST(printf_test, string) {
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr); EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
} }
TEST(printf_test, uchar_string) {
unsigned char str[] = "test";
unsigned char* pstr = str;
EXPECT_EQ("test", fmt::sprintf("%s", pstr));
}
TEST(printf_test, pointer) { TEST(printf_test, pointer) {
int n; int n;
void* p = &n; void* p = &n;
@ -539,10 +532,6 @@ TEST(printf_test, wide_string) {
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
} }
TEST(printf_test, printf_custom) {
EXPECT_EQ("abc", test_sprintf("%s", test_string("abc")));
}
TEST(printf_test, vprintf) { TEST(printf_test, vprintf) {
fmt::format_arg_store<fmt::printf_context, int> as{42}; fmt::format_arg_store<fmt::printf_context, int> as{42};
fmt::basic_format_args<fmt::printf_context> args(as); fmt::basic_format_args<fmt::printf_context> args(as);

17
vendor/Fmt/test/ranges-odr-test.cc vendored Normal file
View File

@ -0,0 +1,17 @@
// Formatting library for C++ - the core API
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include <vector>
#include "fmt/ranges.h"
#include "gtest/gtest.h"
// call fmt::format from another translation unit to test ODR
TEST(ranges_odr_test, format_vector) {
auto v = std::vector<int>{1, 2, 3, 5, 7, 11};
EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]");
}

View File

@ -46,16 +46,42 @@ TEST(ranges_test, format_array_of_literals) {
TEST(ranges_test, format_vector) { TEST(ranges_test, format_vector) {
auto v = std::vector<int>{1, 2, 3, 5, 7, 11}; auto v = std::vector<int>{1, 2, 3, 5, 7, 11};
EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]"); EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]");
EXPECT_EQ(fmt::format("{::#x}", v), "[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]");
} }
TEST(ranges_test, format_vector2) { TEST(ranges_test, format_vector2) {
auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}}; auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}};
EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]"); EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]");
EXPECT_EQ(fmt::format("{:::#x}", v), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]");
} }
TEST(ranges_test, format_map) { TEST(ranges_test, format_map) {
auto m = std::map<std::string, int>{{"one", 1}, {"two", 2}}; auto m = std::map<std::string, int>{{"one", 1}, {"two", 2}};
EXPECT_EQ(fmt::format("{}", m), "[(\"one\", 1), (\"two\", 2)]"); EXPECT_EQ(fmt::format("{}", m), "{\"one\": 1, \"two\": 2}");
}
TEST(ranges_test, format_set) {
EXPECT_EQ(fmt::format("{}", std::set<std::string>{"one", "two"}),
"{\"one\", \"two\"}");
}
namespace adl {
struct box {
int value;
};
auto begin(const box& b) -> const int* {
return &b.value;
}
auto end(const box& b) -> const int* {
return &b.value + 1;
}
} // namespace adl
TEST(ranges_test, format_adl_begin_end) {
auto b = adl::box{42};
EXPECT_EQ(fmt::format("{}", b), "[42]");
} }
TEST(ranges_test, format_pair) { TEST(ranges_test, format_pair) {
@ -126,8 +152,8 @@ TEST(ranges_test, path_like) {
struct string_like { struct string_like {
const char* begin(); const char* begin();
const char* end(); const char* end();
explicit operator fmt::string_view() const { return "foo"; } operator fmt::string_view() const { return "foo"; }
explicit operator std::string_view() const { return "foo"; } operator std::string_view() const { return "foo"; }
}; };
TEST(ranges_test, format_string_like) { TEST(ranges_test, format_string_like) {
@ -190,7 +216,14 @@ TEST(ranges_test, range) {
EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]"); EXPECT_EQ(fmt::format("{}", z), "[0, 0, 0]");
} }
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927 enum test_enum { foo };
TEST(ranges_test, enum_range) {
auto v = std::vector<test_enum>{test_enum::foo};
EXPECT_EQ(fmt::format("{}", v), "[0]");
}
#if !FMT_MSC_VER
struct unformattable {}; struct unformattable {};
TEST(ranges_test, unformattable_range) { TEST(ranges_test, unformattable_range) {
@ -217,6 +250,21 @@ TEST(ranges_test, join_tuple) {
// Single element tuple. // Single element tuple.
auto t4 = std::tuple<float>(4.0f); auto t4 = std::tuple<float>(4.0f);
EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4"); EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4");
# if FMT_TUPLE_JOIN_SPECIFIERS
// Specs applied to each element.
auto t5 = std::tuple<int, int, long>(-3, 100, 1);
EXPECT_EQ(fmt::format("{:+03}", fmt::join(t5, ", ")), "-03, +100, +01");
auto t6 = std::tuple<float, double, long double>(3, 3.14, 3.1415);
EXPECT_EQ(fmt::format("{:5.5f}", fmt::join(t6, ", ")),
"3.00000, 3.14000, 3.14150");
// Testing lvalue tuple args.
int y = -1;
auto t7 = std::tuple<int, int&, const int&>(3, y, y);
EXPECT_EQ(fmt::format("{:03}", fmt::join(t7, ", ")), "003, -01, -01");
# endif
} }
TEST(ranges_test, join_initializer_list) { TEST(ranges_test, join_initializer_list) {
@ -236,9 +284,40 @@ struct zstring {
zstring_sentinel end() const { return {}; } zstring_sentinel end() const { return {}; }
}; };
# ifdef __cpp_lib_ranges
struct cpp20_only_range {
struct iterator {
int val = 0;
using value_type = int;
using difference_type = std::ptrdiff_t;
using iterator_concept = std::input_iterator_tag;
iterator() = default;
iterator(int i) : val(i) {}
int operator*() const { return val; }
iterator& operator++() {
++val;
return *this;
}
void operator++(int) { ++*this; }
bool operator==(const iterator& rhs) const { return val == rhs.val; }
};
int lo;
int hi;
iterator begin() const { return iterator(lo); }
iterator end() const { return iterator(hi); }
};
static_assert(std::input_iterator<cpp20_only_range::iterator>);
# endif
TEST(ranges_test, join_sentinel) { TEST(ranges_test, join_sentinel) {
auto hello = zstring{"hello"}; auto hello = zstring{"hello"};
EXPECT_EQ(fmt::format("{}", hello), "['h', 'e', 'l', 'l', 'o']"); EXPECT_EQ(fmt::format("{}", hello), "['h', 'e', 'l', 'l', 'o']");
EXPECT_EQ(fmt::format("{::}", hello), "[h, e, l, l, o]");
EXPECT_EQ(fmt::format("{}", fmt::join(hello, "_")), "h_e_l_l_o"); EXPECT_EQ(fmt::format("{}", fmt::join(hello, "_")), "h_e_l_l_o");
} }
@ -260,18 +339,62 @@ TEST(ranges_test, join_range) {
const auto z = std::vector<int>(3u, 0); const auto z = std::vector<int>(3u, 0);
EXPECT_EQ(fmt::format("{}", fmt::join(z, ",")), "0,0,0"); EXPECT_EQ(fmt::format("{}", fmt::join(z, ",")), "0,0,0");
# ifdef __cpp_lib_ranges
EXPECT_EQ(fmt::format("{}", cpp20_only_range{.lo = 0, .hi = 5}),
"[0, 1, 2, 3, 4]");
EXPECT_EQ(
fmt::format("{}", fmt::join(cpp20_only_range{.lo = 0, .hi = 5}, ",")),
"0,1,2,3,4");
# endif
} }
#endif // FMT_RANGES_TEST_ENABLE_JOIN #endif // FMT_RANGES_TEST_ENABLE_JOIN
TEST(ranges_test, is_printable) {
using fmt::detail::is_printable;
EXPECT_TRUE(is_printable(0x0323));
EXPECT_FALSE(is_printable(0x0378));
EXPECT_FALSE(is_printable(0x110000));
}
TEST(ranges_test, escape_string) { TEST(ranges_test, escape_string) {
using vec = std::vector<std::string>; using vec = std::vector<std::string>;
EXPECT_EQ(fmt::format("{}", vec{"\n\r\t\"\\"}), "[\"\\n\\r\\t\\\"\\\\\"]"); EXPECT_EQ(fmt::format("{}", vec{"\n\r\t\"\\"}), "[\"\\n\\r\\t\\\"\\\\\"]");
EXPECT_EQ(fmt::format("{}", vec{"\x07"}), "[\"\\x07\"]"); EXPECT_EQ(fmt::format("{}", vec{"\x07"}), "[\"\\x07\"]");
EXPECT_EQ(fmt::format("{}", vec{"\x7f"}), "[\"\\x7f\"]"); EXPECT_EQ(fmt::format("{}", vec{"\x7f"}), "[\"\\x7f\"]");
EXPECT_EQ(fmt::format("{}", vec{"n\xcc\x83"}), "[\"n\xcc\x83\"]");
// Unassigned Unicode code points.
if (fmt::detail::is_utf8()) { if (fmt::detail::is_utf8()) {
EXPECT_EQ(fmt::format("{}", vec{"\xcd\xb8"}), "[\"\\u0378\"]");
// Unassigned Unicode code points.
EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]"); EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]");
EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xbf"}), "[\"\\U0010ffff\"]"); EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}),
"[\"\\xf4\\x8f\\xbf\\xc0\"]");
} }
} }
#ifdef FMT_USE_STRING_VIEW
struct convertible_to_string_view {
operator std::string_view() const { return "foo"; }
};
TEST(ranges_test, escape_convertible_to_string_view) {
EXPECT_EQ(fmt::format("{}", std::vector<convertible_to_string_view>(1)),
"[\"foo\"]");
}
#endif // FMT_USE_STRING_VIEW
template <typename R> struct fmt_ref_view {
R* r;
auto begin() const -> decltype(r->begin()) { return r->begin(); }
auto end() const -> decltype(r->end()) { return r->end(); }
};
TEST(ranges_test, range_of_range_of_mixed_const) {
std::vector<std::vector<int>> v = {{1, 2, 3}, {4, 5}};
EXPECT_EQ(fmt::format("{}", v), "[[1, 2, 3], [4, 5]]");
fmt_ref_view<decltype(v)> r{&v};
EXPECT_EQ(fmt::format("{}", r), "[[1, 2, 3], [4, 5]]");
}

View File

@ -1,161 +0,0 @@
#include <format>
#include "gtest/gtest.h"
TEST(std_format_test, escaping) {
using namespace std;
string s = format("{0}-{{", 8); // s == "8-{"
EXPECT_EQ(s, "8-{");
}
TEST(std_format_test, indexing) {
using namespace std;
string s0 = format("{} to {}", "a", "b"); // OK: automatic indexing
string s1 = format("{1} to {0}", "a", "b"); // OK: manual indexing
EXPECT_EQ(s0, "a to b");
EXPECT_EQ(s1, "b to a");
// Error: mixing automatic and manual indexing
EXPECT_THROW(string s2 = format("{0} to {}", "a", "b"), std::format_error);
// Error: mixing automatic and manual indexing
EXPECT_THROW(string s3 = format("{} to {1}", "a", "b"), std::format_error);
}
TEST(std_format_test, alignment) {
using namespace std;
char c = 120;
string s0 = format("{:6}", 42); // s0 == " 42"
string s1 = format("{:6}", 'x'); // s1 == "x "
string s2 = format("{:*<6}", 'x'); // s2 == "x*****"
string s3 = format("{:*>6}", 'x'); // s3 == "*****x"
string s4 = format("{:*^6}", 'x'); // s4 == "**x***"
// Error: '=' with charT and no integer presentation type
EXPECT_THROW(string s5 = format("{:=6}", 'x'), std::format_error);
string s6 = format("{:6d}", c); // s6 == " 120"
string s7 = format("{:6}", true); // s9 == "true "
EXPECT_EQ(s0, " 42");
EXPECT_EQ(s1, "x ");
EXPECT_EQ(s2, "x*****");
EXPECT_EQ(s3, "*****x");
EXPECT_EQ(s4, "**x***");
EXPECT_EQ(s6, " 120");
EXPECT_EQ(s7, "true ");
}
TEST(std_format_test, float) {
using namespace std;
double inf = numeric_limits<double>::infinity();
double nan = numeric_limits<double>::quiet_NaN();
string s0 = format("{0:} {0:+} {0:-} {0: }", 1); // s0 == "1 +1 1 1"
string s1 = format("{0:} {0:+} {0:-} {0: }", -1); // s1 == "-1 -1 -1 -1"
string s2 =
format("{0:} {0:+} {0:-} {0: }", inf); // s2 == "inf +inf inf inf"
string s3 =
format("{0:} {0:+} {0:-} {0: }", nan); // s3 == "nan +nan nan nan"
EXPECT_EQ(s0, "1 +1 1 1");
EXPECT_EQ(s1, "-1 -1 -1 -1");
EXPECT_EQ(s2, "inf +inf inf inf");
EXPECT_EQ(s3, "nan +nan nan nan");
}
TEST(std_format_test, int) {
using namespace std;
string s0 = format("{}", 42); // s0 == "42"
string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); // s1 == "101010 42 52 2a"
string s2 = format("{0:#x} {0:#X}", 42); // s2 == "0x2a 0X2A"
string s3 = format("{:L}", 1234); // s3 == "1234" (depends on the locale)
EXPECT_EQ(s0, "42");
EXPECT_EQ(s1, "101010 42 52 2a");
EXPECT_EQ(s2, "0x2a 0X2A");
EXPECT_EQ(s3, "1234");
}
#include <format>
enum color { red, green, blue };
const char* color_names[] = {"red", "green", "blue"};
template <> struct std::formatter<color> : std::formatter<const char*> {
auto format(color c, format_context& ctx) {
return formatter<const char*>::format(color_names[c], ctx);
}
};
struct err {};
TEST(std_format_test, formatter) {
std::string s0 = std::format("{}", 42); // OK: library-provided formatter
// std::string s1 = std::format("{}", L"foo"); // Ill-formed: disabled
// formatter
std::string s2 = std::format("{}", red); // OK: user-provided formatter
// std::string s3 = std::format("{}", err{}); // Ill-formed: disabled
// formatter
EXPECT_EQ(s0, "42");
EXPECT_EQ(s2, "red");
}
struct S {
int value;
};
template <> struct std::formatter<S> {
size_t width_arg_id = 0;
// Parses a width argument id in the format { <digit> }.
constexpr auto parse(format_parse_context& ctx) {
constexpr auto is_ascii_digit = [](const char c) {
return c >= '0' && c <= '9';
};
auto iter = ctx.begin();
// auto get_char = [&]() { return iter != ctx.end() ? *iter : 0; };
auto get_char = [&]() { return iter != ctx.end() ? *iter : '\0'; };
if (get_char() != '{') return iter;
++iter;
char c = get_char();
if (!is_ascii_digit(c) || (++iter, get_char()) != '}')
throw format_error("invalid format");
width_arg_id = fmt::detail::to_unsigned(c - '0');
ctx.check_arg_id(width_arg_id);
return ++iter;
}
// Formats S with width given by the argument width_arg_id.
auto format(S s, format_context& ctx) {
int width = visit_format_arg(
[](auto value) -> int {
using type = decltype(value);
if constexpr (!is_integral_v<type> || is_same_v<type, bool>)
throw format_error("width is not integral");
// else if (value < 0 || value > numeric_limits<int>::max())
else if (fmt::detail::is_negative(value) ||
value > numeric_limits<int>::max())
throw format_error("invalid width");
else
return static_cast<int>(value);
},
ctx.arg(width_arg_id));
return format_to(ctx.out(), "{0:{1}}", s.value, width);
}
};
TEST(std_format_test, parsing) {
std::string s = std::format("{0:{1}}", S{42}, 10); // s == " 42"
EXPECT_EQ(s, " 42");
}
#if FMT_USE_INT128
template <> struct std::formatter<__int128_t> : std::formatter<long long> {
auto format(__int128_t n, format_context& ctx) {
// Format as a long long since we only want to check if it is possible to
// specialize formatter for __int128_t.
return formatter<long long>::format(static_cast<long long>(n), ctx);
}
};
TEST(std_format_test, int128) {
__int128_t n = 42;
auto s = std::format("{}", n);
EXPECT_EQ(s, "42");
}
#endif // FMT_USE_INT128

View File

@ -18,7 +18,7 @@ using testing::Contains;
TEST(unicode_test, is_utf8) { EXPECT_TRUE(fmt::detail::is_utf8()); } TEST(unicode_test, is_utf8) { EXPECT_TRUE(fmt::detail::is_utf8()); }
TEST(unicode_test, legacy_locale) { TEST(unicode_test, legacy_locale) {
auto loc = get_locale("ru_RU.CP1251", "Russian.1251"); auto loc = get_locale("ru_RU.CP1251", "Russian_Russia.1251");
if (loc == std::locale::classic()) return; if (loc == std::locale::classic()) return;
auto s = std::string(); auto s = std::string();

View File

@ -13,8 +13,8 @@
#ifdef FMT_MODULE_TEST #ifdef FMT_MODULE_TEST
import fmt; import fmt;
#else #else
#include "fmt/os.h" # include "fmt/os.h"
#endif // FMT_MODULE_TEST #endif // FMT_MODULE_TEST
#ifdef _MSC_VER #ifdef _MSC_VER
# define FMT_VSNPRINTF vsprintf_s # define FMT_VSNPRINTF vsprintf_s

View File

@ -8,14 +8,18 @@
#include "fmt/xchar.h" #include "fmt/xchar.h"
#include <complex> #include <complex>
#include <cwchar>
#include <vector>
#include "fmt/chrono.h" #include "fmt/chrono.h"
#include "fmt/color.h" #include "fmt/color.h"
#include "fmt/ostream.h" #include "fmt/ostream.h"
#include "fmt/ranges.h" #include "fmt/ranges.h"
#include "gtest/gtest.h" #include "gtest-extra.h" // Contains
#include "util.h" // get_locale
using fmt::detail::max_value; using fmt::detail::max_value;
using testing::Contains;
namespace test_ns { namespace test_ns {
template <typename Char> class test_string { template <typename Char> class test_string {
@ -68,8 +72,10 @@ struct explicitly_convertible_to_wstring_view {
}; };
TEST(xchar_test, format_explicitly_convertible_to_wstring_view) { TEST(xchar_test, format_explicitly_convertible_to_wstring_view) {
EXPECT_EQ(L"foo", // Types explicitly convertible to wstring_view are not formattable by
fmt::format(L"{}", explicitly_convertible_to_wstring_view())); // default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_wstring_view>::value, "");
} }
#endif #endif
@ -87,6 +93,10 @@ TEST(xchar_test, format) {
EXPECT_EQ(L"abc1", fmt::format(L"{}c{}", L"ab", 1)); EXPECT_EQ(L"abc1", fmt::format(L"{}c{}", L"ab", 1));
} }
TEST(xchar_test, is_formattable) {
static_assert(!fmt::is_formattable<const wchar_t*>::value, "");
}
TEST(xchar_test, compile_time_string) { TEST(xchar_test, compile_time_string) {
#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L #if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L
EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42)); EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42));
@ -210,6 +220,12 @@ std::wostream& operator<<(std::wostream& os, streamable_enum) {
return os << L"streamable_enum"; return os << L"streamable_enum";
} }
namespace fmt {
template <>
struct formatter<streamable_enum, wchar_t> : basic_ostream_formatter<wchar_t> {
};
} // namespace fmt
enum unstreamable_enum {}; enum unstreamable_enum {};
TEST(xchar_test, enum) { TEST(xchar_test, enum) {
@ -228,14 +244,14 @@ namespace fake_qt {
class QString { class QString {
public: public:
QString(const wchar_t* s) : s_(s) {} QString(const wchar_t* s) : s_(s) {}
const wchar_t* utf16() const FMT_NOEXCEPT { return s_.data(); } const wchar_t* utf16() const noexcept { return s_.data(); }
int size() const FMT_NOEXCEPT { return static_cast<int>(s_.size()); } int size() const noexcept { return static_cast<int>(s_.size()); }
private: private:
std::wstring s_; std::wstring s_;
}; };
fmt::basic_string_view<wchar_t> to_string_view(const QString& s) FMT_NOEXCEPT { fmt::basic_string_view<wchar_t> to_string_view(const QString& s) noexcept {
return {s.utf16(), static_cast<size_t>(s.size())}; return {s.utf16(), static_cast<size_t>(s.size())};
} }
} // namespace fake_qt } // namespace fake_qt
@ -257,6 +273,55 @@ TEST(xchar_test, chrono) {
EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm), EXPECT_EQ(fmt::format("The date is {:%Y-%m-%d %H:%M:%S}.", tm),
"The date is 2016-04-25 11:22:33."); "The date is 2016-04-25 11:22:33.");
EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42))); EXPECT_EQ(L"42s", fmt::format(L"{}", std::chrono::seconds(42)));
EXPECT_EQ(fmt::format(L"{:%F}", tm), L"2016-04-25");
EXPECT_EQ(fmt::format(L"{:%T}", tm), L"11:22:33");
}
std::wstring system_wcsftime(const std::wstring& format, const std::tm* timeptr,
std::locale* locptr = nullptr) {
auto loc = locptr ? *locptr : std::locale::classic();
auto& facet = std::use_facet<std::time_put<wchar_t>>(loc);
std::wostringstream os;
os.imbue(loc);
facet.put(os, os, L' ', timeptr, format.c_str(),
format.c_str() + format.size());
#ifdef _WIN32
// Workaround a bug in older versions of Universal CRT.
auto str = os.str();
if (str == L"-0000") str = L"+0000";
return str;
#else
return os.str();
#endif
}
TEST(chrono_test, time_point) {
auto t1 = std::chrono::system_clock::now();
std::vector<std::wstring> spec_list = {
L"%%", L"%n", L"%t", L"%Y", L"%EY", L"%y", L"%Oy", L"%Ey", L"%C",
L"%EC", L"%G", L"%g", L"%b", L"%h", L"%B", L"%m", L"%Om", L"%U",
L"%OU", L"%W", L"%OW", L"%V", L"%OV", L"%j", L"%d", L"%Od", L"%e",
L"%Oe", L"%a", L"%A", L"%w", L"%Ow", L"%u", L"%Ou", L"%H", L"%OH",
L"%I", L"%OI", L"%M", L"%OM", L"%S", L"%OS", L"%x", L"%Ex", L"%X",
L"%EX", L"%D", L"%F", L"%R", L"%T", L"%p", L"%z", L"%Z"};
spec_list.push_back(L"%Y-%m-%d %H:%M:%S");
#ifndef _WIN32
// Disabled on Windows, because these formats is not consistent among
// platforms.
spec_list.insert(spec_list.end(), {L"%c", L"%Ec", L"%r"});
#endif
for (const auto& spec : spec_list) {
auto t = std::chrono::system_clock::to_time_t(t1);
auto tm = *std::localtime(&t);
auto sys_output = system_wcsftime(spec, &tm);
auto fmt_spec = fmt::format(L"{{:{}}}", spec);
EXPECT_EQ(sys_output, fmt::format(fmt_spec, t1));
EXPECT_EQ(sys_output, fmt::format(fmt_spec, tm));
}
} }
TEST(xchar_test, color) { TEST(xchar_test, color) {
@ -405,7 +470,7 @@ template <class charT> struct formatter<std::complex<double>, charT> {
specs_.precision, specs_.precision_ref, ctx); specs_.precision, specs_.precision_ref, ctx);
auto specs = std::string(); auto specs = std::string();
if (specs_.precision > 0) specs = fmt::format(".{}", specs_.precision); if (specs_.precision > 0) specs = fmt::format(".{}", specs_.precision);
if (specs_.type) specs += specs_.type; if (specs_.type == presentation_type::fixed_lower) specs += 'f';
auto real = fmt::format(ctx.locale().template get<std::locale>(), auto real = fmt::format(ctx.locale().template get<std::locale>(),
fmt::runtime("{:" + specs + "}"), c.real()); fmt::runtime("{:" + specs + "}"), c.real());
auto imag = fmt::format(ctx.locale().template get<std::locale>(), auto imag = fmt::format(ctx.locale().template get<std::locale>(),
@ -426,4 +491,20 @@ TEST(locale_test, complex) {
EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), " (1+2i)"); EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), " (1+2i)");
} }
TEST(locale_test, chrono_weekday) {
auto loc = get_locale("ru_RU.UTF-8", "Russian_Russia.1251");
auto loc_old = std::locale::global(loc);
auto mon = fmt::weekday(1);
EXPECT_EQ(fmt::format(L"{}", mon), L"Mon");
if (loc != std::locale::classic()) {
// {L"\x43F\x43D", L"\x41F\x43D", L"\x43F\x43D\x434", L"\x41F\x43D\x434"}
// {L"пн", L"Пн", L"пнд", L"Пнд"}
EXPECT_THAT(
(std::vector<std::wstring>{L"\x43F\x43D", L"\x41F\x43D",
L"\x43F\x43D\x434", L"\x41F\x43D\x434"}),
Contains(fmt::format(loc, L"{:L}", mon)));
}
std::locale::global(loc_old);
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR #endif // FMT_STATIC_THOUSANDS_SEPARATOR