Squashed 'externals/fmt/' changes from b6f4ceaed..c4ee72653
c4ee72653 Update version fa2eb2d2e Bump version 35f72bf21 Bump version d22f00d7e Update changelog 4e8d21560 Update changelog 84eecb656 Prune CI configs 55727e3b2 More compile-time checks 1010b7f14 Update docs 2ac51fc44 Update changelog 831132293 Workaround for Microsoft Visual Studio 2022 Internal compiler error. 115e00e0b Replace __cplusplus with FMT_CPLUSPLUS. 94114b05c New CI: Microsoft Visual Studio 2022. d2a232082 Fix partial specialization problem for filesystem for Visual Studio (#2957) 0c06c81da Deprecated implicit conversion of enums to ints for consistency with scoped enums c12b4c0cf New CI: GCC-8 C++17, Clang-8 C++17. 99bb5b1d1 Fix std::variant, std::filesystem::path tests on GCC-8, Clang-7,8. e29c2bc60 Update docs c65e4286b Update changelog 69c24e47e Update changelog 6a775e956 Add support for 'std::variant' in C++17 (#2941) 51535866d Update docs 3ef5caa9f Update docs dccd3e674 Fix docs 9cb02aaaa Fix UDLs e6d478f8e Update changelog and docs 2d931b149 Add fmt::streamed 0506a5733 Update changelog e8bd2a804 Fix enable_ifs for map formatter (#2944) 7c56e11ec Update changelog 69a20db08 Update changelog and fix an apidoc comment 7a2a97c88 Update changelog 568233889 Fix is_formattable for tuple-like types. (#2940) f0de12844 Remove /source-charset:utf-8 compile option. eaa8efb95 Fix ofstream handling in msvc fb991e9d3 Update changelog 8e47cfd1c fix -Wsign-conversion warning 247187586 Make the tests pass on a CHERI system. b135f1c01 Refactor handling of argument types f61a1e813 Add format_arg_types 48b7e3daf Added a FMT_STRING wrapper for system_error() call. 4bb3af7a6 Improve compile-time checks d02c582b9 Fix 'duplicate symbol' error. b59d8c3a2 Make std::filesystem::path formatter utf-8 compatible. 232e21d51 Add utf-8 test for std::filesystem::path formatter. 864465419 Docs: add comment about empty format context range ba50c19e8 use qualified call to avoid ADL conflict with std::format_to 9d6039595 Fix compilation on ppc64 a2681aabc Debug ppc failure bfc576736 Add support for std.h in Bazel build 798d09bb7 Debug ppc failure 8c7cf5139 Cleanup cdfacb434 Cleanup parse_format_string 926ddd063 Move compile string to detail cb682f36f Move to_string_view to detail 156744ad4 Simplify fmt::runtime d9c7166cf bi_iterator -> base 11316b29a chore: Set permissions for GitHub actions fe6eb792d Cleanup check_format_string 054b1d980 Remove unused include e927149f8 Cleanup macros 1761e2666 Remove FMT_CONSTEXPR_DECL d6b568a6c Cleanup string_view checks c83a5d42b FMT_MSC_VER -> FMT_MSC_VERSION 27cd68c30 Cleanup macros 08be4abb3 Remove FMT_NVCOMPILER_VERSION 661b19254 Remove FMT_HEADER_ONLY_CONSTEXPR20 d1026fa5d Remove extern format_float 7e63b600b Make to_string work with __float128 b2ea212cd Update README.rst c2fcdc54e Move format_float to format.h for __float128 2b9037a19 Move basic_fp to format.h for compile-time formatting 542785ccb Get rid of detail::bits 65dd2ea52 Use write_escaped_string to std::filesystem::path. 9860f67cd Improve xchar support for std formatters. 03b1b2838 Improve std::filesystem::path formatter. 4f9311e68 Fix definition of error_handler::on_error 652fea45a Visual Studio 2022: fmt/format.h(1526,27): warning C4127: conditional expression is constant #2908 1f9eae7e3 Add xchar support for write_escaped_string. 90b68783f Skip cmake targets inclusion if fmt::fmt already exists (#2907) ce246aaf7 Remove deprecated APIs edeb3d809 Remove deprecated APIs 496aff7c3 Remove deprecated APIs f5cdf7cb0 Simplify snprintf_float 440512f08 Remove deprecated APIs 621eb80bb Remove deprecated APIs 5c7d315de Remove locale.h c6324009b Add initial double-double support 147e8ca58 Fix Windows max mix-up (#2903) 6bf039d75 Add std:🧵:id formatter 9730fb015 Fix path formatter f0903ad9d Add a path formatter 8833f386e Merge branch 'master' of github.com:fmtlib/fmt 5ab9d3925 Namespace-qualify format_to to avoid conflict with std::format_to af5644c27 Update README.rst 3e28dc021 VS2022 17.2: C4189: 'zero': local variable is initialized but not referenced #2891 (#2892) f6f920a1a Tweak a comment and apply clang-format ae963e444 Implement constexpr isfinite to avoid producing NaN 358f5a7e5 Make precision computation consistent with width f63afd161 Fixed all clang -Wsigned-enum-bitfield warnings (#2882) 7e4ad4017 Add initial support for double-double ffb5e6a73 Suppress a -Wliteral-range warning on Apple M1 (#2861) 5d804ee7f Fix handling of subnormals in exotic FP 86e27ccb4 Suppress a warning 192f79aaa Fix handling of locale separators in FP formatting 395cf0f03 Fix detection of unformattable pointers fc429d18b Avoid overhead on sensible platforms ce7ecdb7a Replace conditional compilation with SFINAE 8751a03a0 Fix Unicode handling when writing to an ostream c55175a58 Add an issue template a935ac3e6 MSVC CMake generation optimization (#2852) 22d31b31f Add a __float128 test f607e3e97 Add __float128 support 686de5888 Implement 128-bit constant mul in bigint 02eb215f2 Replace uint128_wrapper with uint128_fallback b4dc7a1d3 Add 128-bit operations to bigint ef54f9aa3 Suppress -Wfloat-equal 288c3b928 Remove dead code in ostream.h format_value 96930161f Implement 128-bit operator+= for uint128_fallback b41890c1e Make arg_mapper SFINAE-friendly again e2408f37c Check if formatter is not defined if there is format_as db5b8993a Fix formatting of std::byte via format_as 1c83eaf75 Fix incompatible between Jinja2 >= 3.1 and sphinx 3.3.0 5379063b5 Fixed clang -Wreserved-identifier warings b591fc87d Fixed all clang -Wreserved-id-macro warnings (on macOS at least) 17dda5839 constexpr -> const for portability 7ffe87c0b Fix docs 3c4273dd0 Simplify UDL 36d95c9fc Fix docs 44abd1f48 Update signatures in docs and ostream.h db745986f Workaround broken std::numeric_limits 8271e43e5 Improve __float128 support and use constexpr 3f9b7433a Improve __float128 support 71778e8b9 Specialize float_info for __float128 f024565c3 Improve exponent handling in Dragon e7f31f5cd Cleanup format_dragon 3c61799fb Cleanup fuzzing mode 4e39e1308 Remove xchar.h include from ostream.h ac0d9d5fe Issue #2816: also strip named-arg for the fallback formatter 4ad90578f Fix #2818: diagnose unformattable arguments in unpacked case 17ba99c1d Fix #2817: add compile-time checking to ostream overloads of fmt::print 3d19be282 Fix #2816: strip named argument wrappers for compile-time checking c076a54a4 Move snprintf_float to format.h 0419d2388 Add FMT_USE_FLOAT128 69396347a Update color.h (#2815) c51604a0e Reduce the number of configs 587dc9946 Remove windows-2016 env no longer suppported by GA 1f3d44b85 Update std::tm/chrono docs bc654faf8 Add is_floating_point that works with __float128 26bffce66 Simplify basic_memory_buffer ed18ca3ea Implement isnan a204b8dde Add initial __float128 support b6b003b07 Cleanup test f2543b0a9 Add initial support for 128-bit floats 72f487562 Simplify float_info f91f61cd1 Reuse num_significand_bits 9a1beab57 Workaround Windows API garbage a8fe8becf Fix compilation error for ranges with ADL `begin`/`end` (#2807) f6bcb25e1 Remove extra dot b4a4189d0 Fix handling of implicit bit 32d477e5f Add `styled` in documentation (#2805) 0b7c045a2 Simplify _cf c10fffecd Make _cf visible in the doc build dcfbe4a77 Document output_file default behavior correctly (#2803) 8c9bc070f Implement styled arguments (#2793) 5bc39d363 Eliminate intel compiler warnings (#2802) e3d688e79 Fix warning C4251: class fmt::v8::file needs to have dll-interface (#2797) 8d4f3e91b Update docs 0cef1f819 Fixing formatting of certain kinds of ranges of ranges. (#2787) 5c0d65640 Fix apt install d416a995e Update README.rst 3f67a1247 Update README.rst cc57e3597 Update godbolt link in the readme (#2789) 86477f7ec Fix size computation 0742606f1 Fix Conversion Warning (#2782) 1ba69fb5a Remove snprintf FP fallback ea6f0bf0e Minor cleanup 1a18a2f3d Fixing "C4127: conditional expression is constant" Visual Studio 2022 warning in pedantic mode (#2783) 4fcacea35 Parameterized fp on significand type cf940ae82 Simplify to_decimal 70dc3de05 Update format.h cbc59ca89 Clear moved from memory buffer ea3d326c6 Fix clang -Wliteral-range warning (#2779) aad44f283 Add fmt::enums::format_as 1319719a5 Add underlying_t af5d8004f Limit Dragonbox to supported FP formats 7b9642096 Remove unused include a0b43bfae Add support for 96-bit long double 2c8cd2db3 Fix handling of zero precision b6d56170f Remove unnecessary inline 05432e570 Use consistent indentation 47da218cc Remove uintptr_fallback 4ddab8901 Merge accumulator into int128_fallback d38f72aff Refactor fallback ints 15c2a3bac int128_t -> int128_opt 532a69a63 Fix handling of 96-bit long double with -m32 d8e1dd4ab improve installing headers ae25f7968 add ability to build Apple framework using CMAKE_FRAMEWORK ce93a66df Implement a fallback uint128_t 6a1346405 Include 128-bit with other signed integers in specifier check 70de324aa Apply 2746 fix for NVidia compiler also (#2770) a1ea3e015 Move built-in formatter specialization to core 161059dd9 Add support for extended precision FP c4c6b42de Bump version 21785040c Fix markup 2b6f7fc7a Add partial support for extended precision FP 0a24a0714 Clz builtin may be not constexpr (Issue #2761) (#2762) ba6f89c76 Update .bazelversion (#2766) 5594edaf6 Address https://github.com/fmtlib/fmt/issues/2763 (#2765) 10e3b83a7 Replace ``make_args_checked`` with ``make_format_args`` (#2760) c48353cb7 Update docs 083510f0f Add FMT_CONSTEXPR to rotr instead dba99bc86 Revert adding constexpr to rotr to satisfy C++11 compilers c04af4bfc Simplify remove_trailing_zeros b348caa9e Remove some C-style casts for consistency c8bd1e646 Simplify remove_trailing_zeros 9b23e9dcb Fix wrong comment/refer to a correct reference 69f2c550a Remove std:: infront of uint32_t/64_t & add constexpr to rotr 9b62310f0 Fix some conversion issues 08d12f31d Fix typo dbddb1d06 Remove literal separator to satisfy some compilers 7dbe3dcde Recover log10_2_significand 10642e608 Optimize remove_trailing_zeros 7b4323e1e Add rotr f1bd6f773 Check r < deltai first, because that is the major branch chosen for short inputs 5d8eb6a1a Reflect the new paper - Change constants appearing in log & division computations - Rename beta_minus_1 to beta 8e2e4d403 Suppress a gcc warning a44716f58 Workaround to Intel compiler (#2758) c71b07016 Add missing const qualifier (#2755) ecd6022c2 Update docs afbcf1e8e Remove legacy C locale wrapper 90325d097 Fix stored type detection e2ba01fcb Fix overload ambiguity in print 17b362f78 Simplify ostream opt-in API a5a7e3a26 Update docs f055ebbd2 Make ostream operators opt in to reduce the risk of ODR violations 8a21e328b Remove problematic constructibility check 31e743d06 Don't use ostream for types convertible to string_view 35c0286cd Simplify byte handling c7173a36a Drop :: and fix formatting 3e8372b96 qualify unqualified calls to format in compile.h (#2742) a34a97cc1 Supporting ? as a string presentation type (#2674) ae1aaaee5 Fix access mode of files created (#2530) (#2733) 1557ab764 Add format_as for enums b00a1eac7 Fixes NVIDIA HPC compiler and Intel ICC compatibility (#2732) a7aecbfca Remove an old mingw workaround dfcc730cb Making target_compile_options PRIVATE, fix #2726, fix #2507 f7a809be6 Clarify the choice of magic numbers and compute the most magic one 09fde7f4b Add fmt::underlying for enum classes 0014024a2 Don't rely on transitive includes c28500556 FMT_NOEXCEPT -> noexcept 6240d0201 Improve comments 925b744ae Improve comments 22b14ff25 Simplify cache recovery 3dc26b44d Make a fallback path more compiler-friendly 2e4038bf5 Simplify lines with __builtin_addcll and friends 76336b4f6 Replace noexcept with FMT_NOEXCEPT 918198348 Fix syntax errors 74097a149 Remove now-unused stuffs 21a1c5338 Fix typo 04eea0f0a Remove now-unused stuffs 35a468ed3 Simplify integer checks 1882a7a2c Replace Dragonbox cache which allows simpler cache recovery & integer checks f4dd1b1b8 Simplify Dragonbox Step 3. 70561ed13 Minimize the usage of built-in 128-bit ints It usually generates slower code than manual handling. cdf1a3b53 Fix codecvt warning (#2408) (#2725) b8b037e93 Fix -Wconversion warning (#2724) 5985f0a7d Fix overflow for chrono durations (#2722) 8f8a1a02d Fix handling of formattable types implicitly convertible to pointers b02e5af52 fmt::join support FMT_COMPILE (#2720) 58fb78239 Improve docs 4fe6129d6 Fix FMT_NOEXCEPT definition c056a009d Docs: Fix link to "Compile-time Format String Checks" section (#2712) 7c12118c1 Deprecate buffered_file::fileno 2a09d468d Use noexcept unconditionally a126b4d88 Check if right shift is arithmetic 9ff91b18c Simplify write_fractional_seconds d9f045fba Fix a UB in chrono c06bef727 Adding comments for range formatting. (#2706) 3c98f1a4c Comment style 6e0f1399d Supporting nested format specs for ranges. (#2673) 0102101ac Make colored print handle UTF-8 (#2701) 4ac5269b4 Update ChangeLog.rst git-subtree-dir: externals/fmt git-subtree-split: c4ee726532178e556d923372f29163bd206d7732
This commit is contained in:
parent
6633089a44
commit
a7f9129f18
58 changed files with 4777 additions and 3816 deletions
6
.github/issue_template.md
vendored
Normal file
6
.github/issue_template.md
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
<!--
|
||||
Please make sure that the problem reproduces on the current master before
|
||||
submitting an issue.
|
||||
If possible please provide a repro on Compiler Explorer:
|
||||
https://godbolt.org/z/fxccbh53W.
|
||||
-->
|
3
.github/workflows/doc.yml
vendored
3
.github/workflows/doc.yml
vendored
|
@ -2,6 +2,9 @@ name: doc
|
|||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# Use Ubuntu 20.04 because doxygen 1.8.13 from Ubuntu 18.04 is broken.
|
||||
|
|
15
.github/workflows/linux.yml
vendored
15
.github/workflows/linux.yml
vendored
|
@ -2,6 +2,9 @@ name: linux
|
|||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
@ -20,6 +23,11 @@ jobs:
|
|||
std: 14
|
||||
install: sudo apt install g++-8
|
||||
os: ubuntu-18.04
|
||||
- cxx: g++-8
|
||||
build_type: Debug
|
||||
std: 17
|
||||
install: sudo apt install g++-8
|
||||
os: ubuntu-18.04
|
||||
- cxx: g++-10
|
||||
build_type: Debug
|
||||
std: 17
|
||||
|
@ -29,6 +37,12 @@ jobs:
|
|||
std: 20
|
||||
os: ubuntu-20.04
|
||||
install: sudo apt install g++-11
|
||||
- cxx: clang++-8
|
||||
build_type: Debug
|
||||
std: 17
|
||||
cxxflags: -stdlib=libc++
|
||||
os: ubuntu-18.04
|
||||
install: sudo apt install clang-8 libc++-8-dev libc++abi-8-dev
|
||||
- cxx: clang++-9
|
||||
build_type: Debug
|
||||
fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON
|
||||
|
@ -52,6 +66,7 @@ jobs:
|
|||
- name: Create Build Environment
|
||||
run: |
|
||||
${{matrix.install}}
|
||||
sudo apt update
|
||||
sudo apt install locales-all
|
||||
cmake -E make_directory ${{runner.workspace}}/build
|
||||
|
||||
|
|
3
.github/workflows/macos.yml
vendored
3
.github/workflows/macos.yml
vendored
|
@ -2,6 +2,9 @@ name: macos
|
|||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-10.15
|
||||
|
|
23
.github/workflows/windows.yml
vendored
23
.github/workflows/windows.yml
vendored
|
@ -2,31 +2,34 @@ name: windows
|
|||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{matrix.os}}
|
||||
strategy:
|
||||
matrix:
|
||||
# windows-2016 and windows-2019 have MSVC 2017 and 2019 installed
|
||||
# respectively: https://github.com/actions/virtual-environments.
|
||||
os: [windows-2016, windows-2019]
|
||||
# windows-2019 has MSVC 2019 installed;
|
||||
# windows-2022 has MSVC 2022 installed:
|
||||
# https://github.com/actions/virtual-environments.
|
||||
os: [windows-2019]
|
||||
platform: [Win32, x64]
|
||||
build_type: [Debug, Release]
|
||||
standard: [11, 17, 20]
|
||||
include:
|
||||
- os: windows-2016
|
||||
- os: windows-2019
|
||||
platform: Win32
|
||||
build_type: Debug
|
||||
shared: -DBUILD_SHARED_LIBS=ON
|
||||
exclude:
|
||||
- os: windows-2016
|
||||
platform: Win32
|
||||
- os: windows-2016
|
||||
standard: 17
|
||||
- os: windows-2016
|
||||
- os: windows-2022
|
||||
platform: x64
|
||||
build_type: Debug
|
||||
standard: 20
|
||||
exclude:
|
||||
- os: windows-2019
|
||||
standard: 11
|
||||
platform: Win32
|
||||
- os: windows-2019
|
||||
standard: 20
|
||||
platform: Win32
|
||||
|
|
|
@ -125,7 +125,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
|||
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
|
||||
|
||||
include(cxx14)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(JoinPaths)
|
||||
|
||||
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
|
||||
|
@ -209,18 +208,6 @@ if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
|||
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
|
||||
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)
|
||||
set(headers ${${VAR}})
|
||||
foreach (header ${ARGN})
|
||||
|
@ -231,7 +218,7 @@ endfunction()
|
|||
|
||||
# Define the fmt library, its includes and the needed defines.
|
||||
add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h
|
||||
format-inl.h locale.h os.h ostream.h printf.h ranges.h
|
||||
format-inl.h os.h ostream.h printf.h ranges.h std.h
|
||||
xchar.h)
|
||||
if (FMT_MODULE)
|
||||
set(FMT_SOURCES src/fmt.cc)
|
||||
|
@ -244,17 +231,6 @@ endif ()
|
|||
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
|
||||
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)
|
||||
target_compile_options(fmt PRIVATE ${WERROR_FLAG})
|
||||
endif ()
|
||||
|
@ -275,6 +251,7 @@ set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
|
|||
|
||||
set_target_properties(fmt PROPERTIES
|
||||
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
|
||||
PUBLIC_HEADER "${FMT_HEADERS}"
|
||||
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
|
||||
|
||||
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
|
||||
|
@ -352,6 +329,8 @@ if (FMT_INSTALL)
|
|||
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
|
||||
LIBRARY DESTINATION ${FMT_LIB_DIR}
|
||||
ARCHIVE DESTINATION ${FMT_LIB_DIR}
|
||||
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
|
||||
FRAMEWORK DESTINATION "."
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
# Use a namespace because CMake provides better diagnostics for namespaced
|
||||
|
@ -368,7 +347,6 @@ if (FMT_INSTALL)
|
|||
|
||||
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
|
||||
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
|
||||
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
|
||||
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
|
||||
endif ()
|
||||
|
||||
|
|
395
ChangeLog.rst
395
ChangeLog.rst
|
@ -1,3 +1,389 @@
|
|||
9.0.0 - 2022-07-04
|
||||
------------------
|
||||
|
||||
* Switched to the internal floating point formatter for all decimal presentation
|
||||
formats. In particular this results in consistent rounding on all platforms
|
||||
and removing the ``s[n]printf`` fallback for decimal FP formatting.
|
||||
|
||||
* Compile-time floating point formatting no longer requires the header-only
|
||||
mode. For example (`godbolt <https://godbolt.org/z/G37PTeG3b>`__):
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <array>
|
||||
#include <fmt/compile.h>
|
||||
|
||||
consteval auto compile_time_dtoa(double value) -> std::array<char, 10> {
|
||||
auto result = std::array<char, 10>();
|
||||
fmt::format_to(result.data(), FMT_COMPILE("{}"), value);
|
||||
return result;
|
||||
}
|
||||
|
||||
constexpr auto answer = compile_time_itoa(0.42);
|
||||
|
||||
works with the default settings.
|
||||
|
||||
* Improved the implementation of
|
||||
`Dragonbox <https://github.com/jk-jeon/dragonbox>`_, the algorithm used for
|
||||
the default floating-point formatting
|
||||
(`#2713 <https://github.com/fmtlib/fmt/pull/2713>`_,
|
||||
`#2750 <https://github.com/fmtlib/fmt/pull/2750>`_).
|
||||
Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_.
|
||||
|
||||
* Made ``fmt::to_string`` work with ``__float128``. This uses the internal
|
||||
FP formatter and works even on system without ``__float128`` support in
|
||||
``[s]printf``.
|
||||
|
||||
* Disabled automatic ``std::ostream`` insertion operator (``operator<<``)
|
||||
discovery when ``fmt/ostream.h`` is included to prevent ODR violations.
|
||||
You can get the old behavior by defining ``FMT_DEPRECATED_OSTREAM`` but this
|
||||
will be removed in the next major release. Use ``fmt::streamed`` or
|
||||
``fmt::ostream_formatter`` to enable formatting via ``std::ostream`` instead.
|
||||
|
||||
* Added ``fmt::ostream_formatter`` that can be used to write ``formatter``
|
||||
specializations that perform formatting via ``std::ostream``.
|
||||
For example (`godbolt <https://godbolt.org/z/5sEc5qMsf>`__):
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
struct date {
|
||||
int year, month, day;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const date& d) {
|
||||
return os << d.year << '-' << d.month << '-' << d.day;
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct fmt::formatter<date> : ostream_formatter {};
|
||||
|
||||
std::string s = fmt::format("The date is {}", date{2012, 12, 9});
|
||||
// s == "The date is 2012-12-9"
|
||||
|
||||
* Added the ``fmt::streamed`` function that takes an object and formats it
|
||||
via ``std::ostream``.
|
||||
For example (`godbolt <https://godbolt.org/z/5G3346G1f>`__):
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <thread>
|
||||
#include <fmt/ostream.h>
|
||||
|
||||
int main() {
|
||||
fmt::print("Current thread id: {}\n",
|
||||
fmt::streamed(std::this_thread::get_id()));
|
||||
}
|
||||
|
||||
Note that ``fmt/std.h`` provides a ``formatter`` specialization for
|
||||
``std::thread::id`` so you don't need to format it via ``std::ostream``.
|
||||
|
||||
* Deprecated implicit conversions of unscoped enums to integers for consistency
|
||||
with scoped enums.
|
||||
|
||||
* Added an argument-dependent lookup based ``format_as`` extension API to
|
||||
simplify formatting of enums.
|
||||
|
||||
* Added experimental ``std::variant`` formatting support
|
||||
(`#2941 <https://github.com/fmtlib/fmt/pull/2941>`_).
|
||||
For example (`godbolt <https://godbolt.org/z/KG9z6cq68>`__):
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <variant>
|
||||
#include <fmt/std.h>
|
||||
|
||||
int main() {
|
||||
auto v = std::variant<int, std::string>(42);
|
||||
fmt::print("{}\n", v);
|
||||
}
|
||||
|
||||
prints::
|
||||
|
||||
variant(42)
|
||||
|
||||
Thanks `@jehelset <https://github.com/jehelset>`_.
|
||||
|
||||
* Added experimental ``std::filesystem::path`` formatting support
|
||||
(`#2865 <https://github.com/fmtlib/fmt/issues/2865>`_,
|
||||
`#2902 <https://github.com/fmtlib/fmt/pull/2902>`_,
|
||||
`#2917 <https://github.com/fmtlib/fmt/issues/2917>`_,
|
||||
`#2918 <https://github.com/fmtlib/fmt/pull/2918>`_).
|
||||
For example (`godbolt <https://godbolt.org/z/o44dMexEb>`__):
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <filesystem>
|
||||
#include <fmt/std.h>
|
||||
|
||||
int main() {
|
||||
fmt::print("There is no place like {}.", std::filesystem::path("/home"));
|
||||
}
|
||||
|
||||
prints::
|
||||
|
||||
There is no place like "/home".
|
||||
|
||||
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
|
||||
|
||||
* Added a ``std::thread::id`` formatter to ``fmt/std.h``.
|
||||
For example (`godbolt <https://godbolt.org/z/j1azbYf3E>`__):
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <thread>
|
||||
#include <fmt/std.h>
|
||||
|
||||
int main() {
|
||||
fmt::print("Current thread id: {}\n", std::this_thread::get_id());
|
||||
}
|
||||
|
||||
* Added ``fmt::styled`` that applies a text style to an individual argument
|
||||
(`#2793 <https://github.com/fmtlib/fmt/pull/2793>`_).
|
||||
For example (`godbolt <https://godbolt.org/z/vWGW7v5M6>`__):
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/color.h>
|
||||
|
||||
int main() {
|
||||
auto now = std::chrono::system_clock::now();
|
||||
fmt::print(
|
||||
"[{}] {}: {}\n",
|
||||
fmt::styled(now, fmt::emphasis::bold),
|
||||
fmt::styled("error", fg(fmt::color::red)),
|
||||
"something went wrong");
|
||||
}
|
||||
|
||||
prints
|
||||
|
||||
.. image:: https://user-images.githubusercontent.com/576385/
|
||||
175071215-12809244-dab0-4005-96d8-7cd911c964d5.png
|
||||
|
||||
Thanks `@rbrugo (Riccardo Brugo) <https://github.com/rbrugo>`_.
|
||||
|
||||
* Made ``fmt::print`` overload for text styles correctly handle UTF-8
|
||||
(`#2681 <https://github.com/fmtlib/fmt/issues/2681>`_,
|
||||
`#2701 <https://github.com/fmtlib/fmt/pull/2701>`_).
|
||||
Thanks `@AlexGuteniev (Alex Guteniev) <https://github.com/AlexGuteniev>`_.
|
||||
|
||||
* Fixed Unicode handling when writing to an ostream.
|
||||
|
||||
* Added support for nested specifiers to range formatting
|
||||
(`#2673 <https://github.com/fmtlib/fmt/pull/2673>`_).
|
||||
For example (`godbolt <https://godbolt.org/z/xd3Gj38cf>`__):
|
||||
|
||||
.. code:: c++
|
||||
|
||||
#include <vector>
|
||||
#include <fmt/ranges.h>
|
||||
|
||||
int main() {
|
||||
fmt::print("{::#x}\n", std::vector{10, 20, 30});
|
||||
}
|
||||
|
||||
prints ``[0xa, 0x14, 0x1e]``.
|
||||
|
||||
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
|
||||
|
||||
* Implemented escaping of wide strings in ranges
|
||||
(`#2904 <https://github.com/fmtlib/fmt/pull/2904>`_).
|
||||
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
|
||||
|
||||
* Added support for ranges with ``begin`` / ``end`` found via the
|
||||
argument-dependent lookup
|
||||
(`#2807 <https://github.com/fmtlib/fmt/pull/2807>`_).
|
||||
Thanks `@rbrugo (Riccardo Brugo) <https://github.com/rbrugo>`_.
|
||||
|
||||
* Fixed formatting of certain kinds of ranges of ranges
|
||||
(`#2787 <https://github.com/fmtlib/fmt/pull/2787>`_).
|
||||
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
|
||||
|
||||
* Fixed handling of maps with element types other than ``std::pair``
|
||||
(`#2944 <https://github.com/fmtlib/fmt/pull/2944>`_).
|
||||
Thanks `@BrukerJWD (Jonathan W) <https://github.com/BrukerJWD>`_.
|
||||
|
||||
* Made tuple formatter enabled only if elements are formattable
|
||||
(`#2939 <https://github.com/fmtlib/fmt/issues/2939>`_,
|
||||
`#2940 <https://github.com/fmtlib/fmt/pull/2940>`_).
|
||||
Thanks `@jehelset <https://github.com/jehelset>`_.
|
||||
|
||||
* Made ``fmt::join`` compatible with format string compilation
|
||||
(`#2719 <https://github.com/fmtlib/fmt/issues/2719>`_,
|
||||
`#2720 <https://github.com/fmtlib/fmt/pull/2720>`_).
|
||||
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
|
||||
|
||||
* Made compile-time checks work with named arguments of custom types and
|
||||
``std::ostream`` ``print`` overloads
|
||||
(`#2816 <https://github.com/fmtlib/fmt/issues/2816>`_,
|
||||
`#2817 <https://github.com/fmtlib/fmt/issues/2817>`_,
|
||||
`#2819 <https://github.com/fmtlib/fmt/pull/2819>`_).
|
||||
Thanks `@timsong-cpp <https://github.com/timsong-cpp>`_.
|
||||
|
||||
* Removed ``make_args_checked`` because it is no longer needed for compile-time
|
||||
checks (`#2760 <https://github.com/fmtlib/fmt/pull/2760>`_).
|
||||
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
|
||||
|
||||
* Removed the following deprecated APIs: ``_format``, ``arg_join``,
|
||||
the ``format_to`` overload that takes a memory buffer,
|
||||
``[v]fprintf`` that takes an ``ostream``.
|
||||
|
||||
* Removed the deprecated implicit conversion of ``[const] signed char*`` and
|
||||
``[const] unsigned char*`` to C strings.
|
||||
|
||||
* Removed the deprecated ``fmt/locale.h``.
|
||||
|
||||
* Replaced the deprecated ``fileno()`` with ``descriptor()`` in
|
||||
``buffered_file``.
|
||||
|
||||
* Moved ``to_string_view`` to the ``detail`` namespace since it's an
|
||||
implementation detail.
|
||||
|
||||
* Made access mode of a created file consistent with ``fopen`` by setting
|
||||
``S_IWGRP`` and ``S_IWOTH``
|
||||
(`#2733 <https://github.com/fmtlib/fmt/pull/2733>`_).
|
||||
Thanks `@arogge (Andreas Rogge) <https://github.com/arogge>`_.
|
||||
|
||||
* Removed a redundant buffer resize when formatting to ``std::ostream``
|
||||
(`#2842 <https://github.com/fmtlib/fmt/issues/2842>`_,
|
||||
`#2843 <https://github.com/fmtlib/fmt/pull/2843>`_).
|
||||
Thanks `@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_.
|
||||
|
||||
* Made precision computation for strings consistent with width
|
||||
(`#2888 <https://github.com/fmtlib/fmt/issues/2888>`_).
|
||||
|
||||
* Fixed handling of locale separators in floating point formatting
|
||||
(`#2830 <https://github.com/fmtlib/fmt/issues/2830>`_).
|
||||
|
||||
* Made sign specifiers work with ``__int128_t``
|
||||
(`#2773 <https://github.com/fmtlib/fmt/issues/2773>`_).
|
||||
|
||||
* Improved support for systems such as CHERI with extra data stored in pointers
|
||||
(`#2932 <https://github.com/fmtlib/fmt/pull/2932>`_).
|
||||
Thanks `@davidchisnall (David Chisnall) <https://github.com/davidchisnall>`_.
|
||||
|
||||
* Improved documentation
|
||||
(`#2706 <https://github.com/fmtlib/fmt/pull/2706>`_,
|
||||
`#2712 <https://github.com/fmtlib/fmt/pull/2712>`_,
|
||||
`#2789 <https://github.com/fmtlib/fmt/pull/2789>`_,
|
||||
`#2803 <https://github.com/fmtlib/fmt/pull/2803>`_,
|
||||
`#2805 <https://github.com/fmtlib/fmt/pull/2805>`_,
|
||||
`#2815 <https://github.com/fmtlib/fmt/pull/2815>`_,
|
||||
`#2924 <https://github.com/fmtlib/fmt/pull/2924>`_).
|
||||
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_,
|
||||
`@Pokechu22 <https://github.com/Pokechu22>`_,
|
||||
`@setoye (Alta) <https://github.com/setoye>`_,
|
||||
`@rtobar <https://github.com/rtobar>`_,
|
||||
`@rbrugo (Riccardo Brugo) <https://github.com/rbrugo>`_,
|
||||
`@anoonD (cre) <https://github.com/anoonD>`_,
|
||||
`@leha-bot (Alex) <https://github.com/leha-bot>`_.
|
||||
|
||||
* Improved build configuration
|
||||
(`#2766 <https://github.com/fmtlib/fmt/pull/2766>`_,
|
||||
`#2772 <https://github.com/fmtlib/fmt/pull/2772>`_,
|
||||
`#2836 <https://github.com/fmtlib/fmt/pull/2836>`_,
|
||||
`#2852 <https://github.com/fmtlib/fmt/pull/2852>`_,
|
||||
`#2907 <https://github.com/fmtlib/fmt/pull/2907>`_,
|
||||
`#2913 <https://github.com/fmtlib/fmt/pull/2913>`_,
|
||||
`#2914 <https://github.com/fmtlib/fmt/pull/2914>`_).
|
||||
Thanks `@kambala-decapitator (Andrey Filipenkov)
|
||||
<https://github.com/kambala-decapitator>`_,
|
||||
`@mattiasljungstrom (Mattias Ljungström)
|
||||
<https://github.com/mattiasljungstrom>`_,
|
||||
`@kieselnb (Nick Kiesel) <https://github.com/kieselnb>`_,
|
||||
`@nathannaveen <https://github.com/nathannaveen>`_,
|
||||
`@Vertexwahn <https://github.com/Vertexwahn>`_.
|
||||
|
||||
* Fixed various warnings and compilation issues
|
||||
(`#2408 <https://github.com/fmtlib/fmt/issues/2408>`_,
|
||||
`#2507 <https://github.com/fmtlib/fmt/issues/2507>`_,
|
||||
`#2697 <https://github.com/fmtlib/fmt/issues/2697>`_,
|
||||
`#2715 <https://github.com/fmtlib/fmt/issues/2715>`_,
|
||||
`#2717 <https://github.com/fmtlib/fmt/issues/2717>`_,
|
||||
`#2722 <https://github.com/fmtlib/fmt/pull/2722>`_,
|
||||
`#2724 <https://github.com/fmtlib/fmt/pull/2724>`_,
|
||||
`#2725 <https://github.com/fmtlib/fmt/pull/2725>`_,
|
||||
`#2726 <https://github.com/fmtlib/fmt/issues/2726>`_,
|
||||
`#2728 <https://github.com/fmtlib/fmt/pull/2728>`_,
|
||||
`#2732 <https://github.com/fmtlib/fmt/pull/2732>`_,
|
||||
`#2738 <https://github.com/fmtlib/fmt/issues/2738>`_,
|
||||
`#2742 <https://github.com/fmtlib/fmt/pull/2742>`_,
|
||||
`#2744 <https://github.com/fmtlib/fmt/issues/2744>`_,
|
||||
`#2745 <https://github.com/fmtlib/fmt/issues/2745>`_,
|
||||
`#2746 <https://github.com/fmtlib/fmt/issues/2746>`_,
|
||||
`#2754 <https://github.com/fmtlib/fmt/issues/2754>`_,
|
||||
`#2755 <https://github.com/fmtlib/fmt/pull/2755>`_,
|
||||
`#2757 <https://github.com/fmtlib/fmt/issues/2757>`_,
|
||||
`#2758 <https://github.com/fmtlib/fmt/pull/2758>`_,
|
||||
`#2761 <https://github.com/fmtlib/fmt/issues/2761>`_,
|
||||
`#2762 <https://github.com/fmtlib/fmt/pull/2762>`_,
|
||||
`#2763 <https://github.com/fmtlib/fmt/issues/2763>`_,
|
||||
`#2765 <https://github.com/fmtlib/fmt/pull/2765>`_,
|
||||
`#2769 <https://github.com/fmtlib/fmt/issues/2769>`_,
|
||||
`#2770 <https://github.com/fmtlib/fmt/pull/2770>`_,
|
||||
`#2771 <https://github.com/fmtlib/fmt/issues/2771>`_,
|
||||
`#2777 <https://github.com/fmtlib/fmt/issues/2777>`_,
|
||||
`#2779 <https://github.com/fmtlib/fmt/pull/2779>`_,
|
||||
`#2782 <https://github.com/fmtlib/fmt/pull/2782>`_,
|
||||
`#2783 <https://github.com/fmtlib/fmt/pull/2783>`_,
|
||||
`#2794 <https://github.com/fmtlib/fmt/issues/2794>`_,
|
||||
`#2796 <https://github.com/fmtlib/fmt/issues/2796>`_,
|
||||
`#2797 <https://github.com/fmtlib/fmt/pull/2797>`_,
|
||||
`#2801 <https://github.com/fmtlib/fmt/pull/2801>`_,
|
||||
`#2802 <https://github.com/fmtlib/fmt/pull/2802>`_,
|
||||
`#2808 <https://github.com/fmtlib/fmt/issues/2808>`_,
|
||||
`#2818 <https://github.com/fmtlib/fmt/issues/2818>`_,
|
||||
`#2819 <https://github.com/fmtlib/fmt/pull/2819>`_,
|
||||
`#2829 <https://github.com/fmtlib/fmt/issues/2829>`_,
|
||||
`#2835 <https://github.com/fmtlib/fmt/issues/2835>`_,
|
||||
`#2848 <https://github.com/fmtlib/fmt/issues/2848>`_,
|
||||
`#2860 <https://github.com/fmtlib/fmt/issues/2860>`_,
|
||||
`#2861 <https://github.com/fmtlib/fmt/pull/2861>`_,
|
||||
`#2882 <https://github.com/fmtlib/fmt/pull/2882>`_,
|
||||
`#2886 <https://github.com/fmtlib/fmt/issues/2886>`_,
|
||||
`#2891 <https://github.com/fmtlib/fmt/issues/2891>`_,
|
||||
`#2892 <https://github.com/fmtlib/fmt/pull/2892>`_,
|
||||
`#2895 <https://github.com/fmtlib/fmt/issues/2895>`_,
|
||||
`#2896 <https://github.com/fmtlib/fmt/issues/2896>`_,
|
||||
`#2903 <https://github.com/fmtlib/fmt/pull/2903>`_,
|
||||
`#2906 <https://github.com/fmtlib/fmt/issues/2906>`_,
|
||||
`#2908 <https://github.com/fmtlib/fmt/issues/2908>`_,
|
||||
`#2909 <https://github.com/fmtlib/fmt/pull/2909>`_,
|
||||
`#2920 <https://github.com/fmtlib/fmt/issues/2920>`_,
|
||||
`#2922 <https://github.com/fmtlib/fmt/pull/2922>`_,
|
||||
`#2927 <https://github.com/fmtlib/fmt/pull/2927>`_,
|
||||
`#2929 <https://github.com/fmtlib/fmt/pull/2929>`_,
|
||||
`#2936 <https://github.com/fmtlib/fmt/issues/2936>`_,
|
||||
`#2937 <https://github.com/fmtlib/fmt/pull/2937>`_,
|
||||
`#2938 <https://github.com/fmtlib/fmt/pull/2938>`_,
|
||||
`#2951 <https://github.com/fmtlib/fmt/pull/2951>`_,
|
||||
`#2954 <https://github.com/fmtlib/fmt/issues/2954>`_,
|
||||
`#2957 <https://github.com/fmtlib/fmt/pull/2957>`_,
|
||||
`#2958 <https://github.com/fmtlib/fmt/issues/2958>`_,
|
||||
`#2960 <https://github.com/fmtlib/fmt/pull/2960>`_).
|
||||
Thanks `@matrackif <https://github.com/matrackif>`_
|
||||
`@Tobi823 (Tobias Hellmann) <https://github.com/Tobi823>`_,
|
||||
`@ivan-volnov (Ivan Volnov) <https://github.com/ivan-volnov>`_,
|
||||
`@VasiliPupkin256 <https://github.com/VasiliPupkin256>`_,
|
||||
`@federico-busato (Federico) <https://github.com/federico-busato>`_,
|
||||
`@barcharcraz (Charlie Barto) <https://github.com/barcharcraz>`_,
|
||||
`@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_,
|
||||
`@HazardyKnusperkeks (Björn Schäpers)
|
||||
<https://github.com/HazardyKnusperkeks>`_,
|
||||
`@dalboris (Boris Dalstein) <https://github.com/dalboris>`_,
|
||||
`@seanm (Sean McBride) <https://github.com/seanm>`_,
|
||||
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
|
||||
`@timsong-cpp <https://github.com/timsong-cpp>`_,
|
||||
`@seanm (Sean McBride) <https://github.com/seanm>`_,
|
||||
`@frithrah <https://github.com/frithrah>`_,
|
||||
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
|
||||
`@Agga <https://github.com/Agga>`_,
|
||||
`@madmaxoft (Mattes D) <https://github.com/madmaxoft>`_,
|
||||
`@JurajX (Juraj) <https://github.com/JurajX>`_,
|
||||
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
|
||||
`@Dani-Hub (Daniel Krügler) <https://github.com/Dani-Hub>`_.
|
||||
|
||||
8.1.1 - 2022-01-06
|
||||
------------------
|
||||
|
||||
|
@ -6,7 +392,7 @@
|
|||
`#2696 <https://github.com/fmtlib/fmt/pull/2696>`_).
|
||||
Thanks `@saraedum (Julian Rüth) <https://github.com/saraedum>`_.
|
||||
|
||||
* Fixed chorno formatting on big endian systems
|
||||
* 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
|
||||
|
@ -148,6 +534,10 @@
|
|||
["
|
||||
aan"]
|
||||
|
||||
* Added an experimental ``?`` specifier for escaping strings.
|
||||
(`#2674 <https://github.com/fmtlib/fmt/pull/2674>`_).
|
||||
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
|
||||
|
||||
* Switched to JSON-like representation of maps and sets for consistency with
|
||||
Python's ``str.format``.
|
||||
For example (`godbolt <https://godbolt.org/z/seKjoY9W5>`__):
|
||||
|
@ -367,7 +757,8 @@
|
|||
`@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>`_.
|
||||
`@HazardyKnusperkeks (Björn Schäpers)
|
||||
<https://github.com/HazardyKnusperkeks>`_.
|
||||
|
||||
8.0.1 - 2021-07-02
|
||||
------------------
|
||||
|
|
20
README.rst
20
README.rst
|
@ -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
|
||||
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux
|
||||
|
@ -26,12 +28,13 @@
|
|||
**{fmt}** is an open-source formatting library providing a fast and safe
|
||||
alternative to C stdio and C++ iostreams.
|
||||
|
||||
If you like this project, please consider donating to the BYSOL
|
||||
Foundation that helps victims of political repressions in Belarus:
|
||||
https://bysol.org/en/bs/general/.
|
||||
If you like this project, please consider donating to one of the funds that
|
||||
help victims of the war in Ukraine: https://www.stopputin.net/.
|
||||
|
||||
`Documentation <https://fmt.dev>`__
|
||||
|
||||
`Cheat Sheets <https://hackingcpp.com/cpp/libs/fmt.html>`__
|
||||
|
||||
Q&A: ask questions on `StackOverflow with the tag fmt
|
||||
<https://stackoverflow.com/questions/tagged/fmt>`_.
|
||||
|
||||
|
@ -123,7 +126,7 @@ Output::
|
|||
Default format: 42s 100ms
|
||||
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++
|
||||
|
||||
|
@ -341,9 +344,12 @@ Projects using this library
|
|||
|
||||
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
|
||||
|
||||
* `GemRB <https://gemrb.org/>`_: a portable open-source implementation of
|
||||
Bioware’s Infinity Engine
|
||||
|
||||
* `Grand Mountain Adventure
|
||||
<https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/>`_:
|
||||
A beautiful open-world ski & snowboarding game
|
||||
a beautiful open-world ski & snowboarding game
|
||||
|
||||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||
Player vs Player Gaming Network with tweaks
|
||||
|
|
141
doc/api.rst
141
doc/api.rst
|
@ -12,6 +12,7 @@ The {fmt} library API consists of the following parts:
|
|||
formatting functions and locale support
|
||||
* :ref:`fmt/ranges.h <ranges-api>`: formatting of ranges and tuples
|
||||
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
|
||||
* :ref:`fmt/std.h <std-api>`: formatters for standard library types
|
||||
* :ref:`fmt/compile.h <compile-api>`: format string compilation
|
||||
* :ref:`fmt/color.h <color-api>`: terminal color and text style
|
||||
* :ref:`fmt/os.h <os-api>`: system APIs
|
||||
|
@ -66,7 +67,7 @@ checked at compile time in C++20. To pass a runtime format string wrap it in
|
|||
.. doxygenfunction:: print(std::FILE *f, format_string<T...> fmt, T&&... args)
|
||||
.. doxygenfunction:: vprint(std::FILE *f, string_view fmt, format_args args)
|
||||
|
||||
Compile-time Format String Checks
|
||||
Compile-Time Format String Checks
|
||||
---------------------------------
|
||||
|
||||
Compile-time checks are enabled when using ``FMT_STRING``. They support built-in
|
||||
|
@ -113,8 +114,7 @@ binary footprint, for example (https://godbolt.org/z/oba4Mc):
|
|||
|
||||
template <typename S, typename... Args>
|
||||
void log(const char* file, int line, const S& format, Args&&... args) {
|
||||
vlog(file, line, format,
|
||||
fmt::make_args_checked<Args...>(format, args...));
|
||||
vlog(file, line, format, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
#define MY_LOG(format, ...) \
|
||||
|
@ -125,8 +125,6 @@ binary footprint, for example (https://godbolt.org/z/oba4Mc):
|
|||
Note that ``vlog`` is not parameterized on argument types which improves compile
|
||||
times and reduces binary code size compared to a fully parameterized version.
|
||||
|
||||
.. doxygenfunction:: fmt::make_args_checked(const S&, const remove_reference_t<Args>&...)
|
||||
|
||||
.. doxygenfunction:: fmt::make_format_args(const Args&...)
|
||||
|
||||
.. doxygenclass:: fmt::format_arg_store
|
||||
|
@ -143,6 +141,9 @@ times and reduces binary code size compared to a fully parameterized version.
|
|||
.. doxygenclass:: fmt::basic_format_arg
|
||||
:members:
|
||||
|
||||
.. doxygenclass:: fmt::basic_format_parse_context
|
||||
:members:
|
||||
|
||||
.. doxygenclass:: fmt::basic_format_context
|
||||
:members:
|
||||
|
||||
|
@ -179,9 +180,15 @@ functions and locale support.
|
|||
|
||||
.. _udt:
|
||||
|
||||
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``, :ref:`fmt/chrono.h <chrono-api>` for date
|
||||
and time formatting and :ref:`fmt/std.h <std-api>` for path and variant
|
||||
formatting.
|
||||
|
||||
To make a user-defined type formattable, specialize the ``formatter<T>`` struct
|
||||
template and implement ``parse`` and ``format`` methods::
|
||||
|
||||
|
@ -208,6 +215,10 @@ template and implement ``parse`` and ``format`` methods::
|
|||
// the formatter should parse the 'f' specifier and return an iterator
|
||||
// pointing to '}'.
|
||||
|
||||
// Please also note that this character range may be empty, in case of
|
||||
// the "{}" format string, so therefore you should check ctx.begin()
|
||||
// for equality with ctx.end().
|
||||
|
||||
// Parse the presentation format and store it in the formatter:
|
||||
auto it = ctx.begin(), end = ctx.end();
|
||||
if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
|
||||
|
@ -222,11 +233,11 @@ template and implement ``parse`` and ``format`` methods::
|
|||
// Formats the point p using the parsed format specification (presentation)
|
||||
// stored in this formatter.
|
||||
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.
|
||||
return presentation == 'f'
|
||||
? format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y)
|
||||
: format_to(ctx.out(), "({:.1e}, {:.1e})", p.x, p.y);
|
||||
? fmt::format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y)
|
||||
: fmt::format_to(ctx.out(), "({:.1e}, {:.1e})", p.x, p.y);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -244,7 +255,7 @@ example::
|
|||
template <> struct fmt::formatter<color>: formatter<string_view> {
|
||||
// parse is inherited from formatter<string_view>.
|
||||
template <typename FormatContext>
|
||||
auto format(color c, FormatContext& ctx) {
|
||||
auto format(color c, FormatContext& ctx) const {
|
||||
string_view name = "unknown";
|
||||
switch (c) {
|
||||
case color::red: name = "red"; break;
|
||||
|
@ -282,7 +293,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>> :
|
||||
fmt::formatter<std::string> {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
@ -297,17 +308,32 @@ If a type provides both a ``formatter`` specialization and an implicit
|
|||
conversion to a formattable type, the specialization takes precedence over the
|
||||
conversion.
|
||||
|
||||
.. doxygenclass:: fmt::basic_format_parse_context
|
||||
:members:
|
||||
For enums {fmt} also provides the ``format_as`` extension API. To format an enum
|
||||
via this API define ``format_as`` that takes this enum and converts it to the
|
||||
underlying type. ``format_as`` should be defined in the same namespace as the
|
||||
enum.
|
||||
|
||||
Literal-based API
|
||||
Example (https://godbolt.org/z/r7vvGE1v7)::
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace kevin_namespacy {
|
||||
enum class film {
|
||||
house_of_cards, american_beauty, se7en = 7
|
||||
};
|
||||
auto format_as(film f) { return fmt::underlying(f); }
|
||||
}
|
||||
|
||||
int main() {
|
||||
fmt::print("{}\n", kevin_namespacy::film::se7en); // prints "7"
|
||||
}
|
||||
|
||||
Literal-Based API
|
||||
-----------------
|
||||
|
||||
The following user-defined literals are defined in ``fmt/format.h``.
|
||||
|
||||
.. doxygenfunction:: operator""_format(const char *s, size_t n) -> detail::udl_formatter<char>
|
||||
|
||||
.. doxygenfunction:: operator""_a(const char *s, size_t) -> detail::udl_arg<char>
|
||||
.. doxygenfunction:: operator""_a()
|
||||
|
||||
Utilities
|
||||
---------
|
||||
|
@ -316,9 +342,9 @@ Utilities
|
|||
.. doxygenfunction:: fmt::ptr(const std::unique_ptr<T> &p) -> const void*
|
||||
.. doxygenfunction:: fmt::ptr(const std::shared_ptr<T> &p) -> const void*
|
||||
|
||||
.. doxygenfunction:: fmt::to_string(const T &value) -> std::string
|
||||
.. doxygenfunction:: fmt::underlying(Enum e) -> typename std::underlying_type<Enum>::type
|
||||
|
||||
.. doxygenfunction:: fmt::to_string_view(const Char *s) -> basic_string_view<Char>
|
||||
.. doxygenfunction:: fmt::to_string(const T &value) -> std::string
|
||||
|
||||
.. doxygenfunction:: fmt::join(Range &&range, string_view sep) -> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>>
|
||||
|
||||
|
@ -381,8 +407,8 @@ non-default floating-point formatting that occasionally falls back on
|
|||
|
||||
.. _ranges-api:
|
||||
|
||||
Ranges and Tuple Formatting
|
||||
===========================
|
||||
Range and Tuple Formatting
|
||||
==========================
|
||||
|
||||
The library also supports convenient formatting of ranges and tuples::
|
||||
|
||||
|
@ -441,24 +467,56 @@ The format syntax is described in :ref:`chrono-specs`.
|
|||
|
||||
.. doxygenfunction:: gmtime(std::time_t time)
|
||||
|
||||
.. _std-api:
|
||||
|
||||
Standard Library Types Formatting
|
||||
=================================
|
||||
|
||||
``fmt/std.h`` provides formatters for:
|
||||
|
||||
* `std::filesystem::path <std::filesystem::path>`_
|
||||
* `std::thread::id <https://en.cppreference.com/w/cpp/thread/thread/id>`_
|
||||
* `std::monostate <https://en.cppreference.com/w/cpp/utility/variant/monostate>`_
|
||||
* `std::variant <https://en.cppreference.com/w/cpp/utility/variant/variant>`_
|
||||
|
||||
Formatting Variants
|
||||
-------------------
|
||||
|
||||
A ``std::variant`` is only formattable if every variant alternative is formattable, and requires the
|
||||
``__cpp_lib_variant`` `library feature <https://en.cppreference.com/w/cpp/feature_test>`_.
|
||||
|
||||
**Example**::
|
||||
|
||||
#include <fmt/std.h>
|
||||
|
||||
std::variant<char, float> v0{'x'};
|
||||
// Prints "variant('x')"
|
||||
fmt::print("{}", v0);
|
||||
|
||||
std::variant<std::monostate, char> v1;
|
||||
// Prints "variant(monostate)"
|
||||
|
||||
.. _compile-api:
|
||||
|
||||
Format string compilation
|
||||
Format String Compilation
|
||||
=========================
|
||||
|
||||
``fmt/compile.h`` provides format string compilation support when using
|
||||
``FMT_COMPILE``. Format strings are parsed, checked and converted into efficient
|
||||
formatting code at compile-time. This supports arguments of built-in and string
|
||||
types as well as user-defined types with ``constexpr`` ``parse`` functions in
|
||||
their ``formatter`` specializations. Format string compilation can generate more
|
||||
binary code compared to the default API and is only recommended in places where
|
||||
formatting is a performance bottleneck.
|
||||
``fmt/compile.h`` provides format string compilation enabled via the
|
||||
``FMT_COMPILE`` macro or the ``_cf`` user-defined literal. Format strings
|
||||
marked with ``FMT_COMPILE`` or ``_cf`` are parsed, checked and converted into
|
||||
efficient formatting code at compile-time. This supports arguments of built-in
|
||||
and string types as well as user-defined types with ``constexpr`` ``parse``
|
||||
functions in their ``formatter`` specializations. Format string compilation can
|
||||
generate more binary code compared to the default API and is only recommended in
|
||||
places where formatting is a performance bottleneck.
|
||||
|
||||
.. doxygendefine:: FMT_COMPILE
|
||||
|
||||
.. doxygenfunction:: operator""_cf()
|
||||
|
||||
.. _color-api:
|
||||
|
||||
Terminal color and text style
|
||||
Terminal Color and Text Style
|
||||
=============================
|
||||
|
||||
``fmt/color.h`` provides support for terminal color and text style output.
|
||||
|
@ -469,6 +527,8 @@ Terminal color and text style
|
|||
|
||||
.. doxygenfunction:: bg(detail::color_type)
|
||||
|
||||
.. doxygenfunction:: styled(const T& value, text_style ts)
|
||||
|
||||
.. _os-api:
|
||||
|
||||
System APIs
|
||||
|
@ -486,27 +546,28 @@ System APIs
|
|||
========================
|
||||
|
||||
``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>
|
||||
|
||||
class date {
|
||||
int year_, month_, day_;
|
||||
public:
|
||||
date(int year, int month, int day): year_(year), month_(month), day_(day) {}
|
||||
struct date {
|
||||
int year, month, day;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const date& d) {
|
||||
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
|
||||
return os << d.year << '-' << d.month << '-' << d.day;
|
||||
}
|
||||
};
|
||||
|
||||
std::string s = fmt::format("The date is {}", date(2012, 12, 9));
|
||||
template <> struct fmt::formatter<date> : ostream_formatter {};
|
||||
|
||||
std::string s = fmt::format("The date is {}", date{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:: streamed(const T &)
|
||||
|
||||
.. doxygenfunction:: print(std::basic_ostream<Char> &os, const S &format_str, Args&&... args)
|
||||
.. doxygenfunction:: print(std::ostream &os, format_string<T...> fmt, T&&... args)
|
||||
|
||||
.. _printf-api:
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import errno, os, re, sys
|
||||
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', '8.1.0', '8.1.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', '9.0.0']
|
||||
|
||||
class Pip:
|
||||
def __init__(self, venv_dir):
|
||||
|
@ -28,6 +28,9 @@ def create_build_env(venv_dir='virtualenv'):
|
|||
pip.install('six')
|
||||
# See: https://github.com/sphinx-doc/sphinx/issues/9777
|
||||
pip.install('docutils==0.17.1')
|
||||
# Jinja2 >= 3.1 incompatible with sphinx 3.3.0
|
||||
# See: https://github.com/sphinx-doc/sphinx/issues/10291
|
||||
pip.install('Jinja2<3.1')
|
||||
pip.install('sphinx-doc/sphinx', 'v3.3.0')
|
||||
pip.install('michaeljones/breathe', 'v4.25.0')
|
||||
|
||||
|
@ -65,6 +68,7 @@ def build_docs(version='dev', **kwargs):
|
|||
FMT_USE_RVALUE_REFERENCES=1 \
|
||||
FMT_USE_USER_DEFINED_LITERALS=1 \
|
||||
FMT_USE_ALIAS_TEMPLATES=1 \
|
||||
FMT_USE_NONTYPE_TEMPLATE_ARGS=1 \
|
||||
FMT_API= \
|
||||
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \
|
||||
"FMT_END_NAMESPACE=}}" \
|
||||
|
|
|
@ -101,7 +101,7 @@ The code
|
|||
format(FMT_STRING("The answer is {:d}"), "forty-two");
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -304,7 +304,8 @@ The available presentation types for pointers are:
|
|||
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
|
||||
chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`]
|
||||
|
@ -345,12 +346,38 @@ points are:
|
|||
| | command ``%OS`` produces the locale's alternative representation. |
|
||||
+---------+--------------------------------------------------------------------+
|
||||
|
||||
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.
|
||||
|
||||
``std::tm`` uses the system's `strftime
|
||||
<https://en.cppreference.com/w/cpp/chrono/c/strftime>`_ so refer to its
|
||||
documentation for details on supported conversion specifiers.
|
||||
.. range-specs:
|
||||
|
||||
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:
|
||||
|
||||
|
|
|
@ -95,8 +95,8 @@ class dynamic_format_arg_store
|
|||
};
|
||||
|
||||
template <typename T>
|
||||
using stored_type = conditional_t<detail::is_string<T>::value &&
|
||||
!has_formatter<T, Context>::value &&
|
||||
using stored_type = conditional_t<
|
||||
std::is_convertible<T, std::basic_string<char_type>>::value &&
|
||||
!detail::is_reference_wrapper<T>::value,
|
||||
std::basic_string<char_type>, T>;
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath> // std::isfinite
|
||||
#include <cstring> // std::memcpy
|
||||
#include <ctime>
|
||||
#include <iterator>
|
||||
#include <locale>
|
||||
|
@ -321,14 +323,13 @@ constexpr const size_t codecvt_result<CodeUnit>::max_size;
|
|||
template <typename CodeUnit>
|
||||
void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
|
||||
const std::locale& loc) {
|
||||
using codecvt = std::codecvt<CodeUnit, char, std::mbstate_t>;
|
||||
#if FMT_CLANG_VERSION
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated"
|
||||
auto& f = std::use_facet<codecvt>(loc);
|
||||
auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
|
||||
# pragma clang diagnostic pop
|
||||
#else
|
||||
auto& f = std::use_facet<codecvt>(loc);
|
||||
auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
|
||||
#endif
|
||||
auto mb = std::mbstate_t();
|
||||
const char* from_next = nullptr;
|
||||
|
@ -344,7 +345,7 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
|
|||
if (detail::is_utf8() && loc != get_classic_locale()) {
|
||||
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
|
||||
// gcc-4.
|
||||
#if FMT_MSC_VER != 0 || \
|
||||
#if FMT_MSC_VERSION != 0 || \
|
||||
(defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
|
||||
// The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
|
||||
// and newer.
|
||||
|
@ -468,7 +469,7 @@ inline std::tm localtime(std::time_t time) {
|
|||
|
||||
bool fallback(int res) { return res == 0; }
|
||||
|
||||
#if !FMT_MSC_VER
|
||||
#if !FMT_MSC_VERSION
|
||||
bool fallback(detail::null<>) {
|
||||
using namespace fmt::detail;
|
||||
std::tm* tm = std::localtime(&time_);
|
||||
|
@ -514,7 +515,7 @@ inline std::tm gmtime(std::time_t time) {
|
|||
|
||||
bool fallback(int res) { return res == 0; }
|
||||
|
||||
#if !FMT_MSC_VER
|
||||
#if !FMT_MSC_VERSION
|
||||
bool fallback(detail::null<>) {
|
||||
std::tm* tm = std::gmtime(&time_);
|
||||
if (tm) tm_ = *tm;
|
||||
|
@ -562,10 +563,10 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
|
|||
constexpr const size_t len = 8;
|
||||
if (const_check(is_big_endian())) {
|
||||
char tmp[len];
|
||||
memcpy(tmp, &digits, len);
|
||||
std::memcpy(tmp, &digits, len);
|
||||
std::reverse_copy(tmp, tmp + len, buf);
|
||||
} else {
|
||||
memcpy(buf, &digits, len);
|
||||
std::memcpy(buf, &digits, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1214,7 +1215,7 @@ template <typename OutputIt, typename Char> class tm_writer {
|
|||
char buf[10];
|
||||
size_t offset = 0;
|
||||
if (year >= 0 && year < 10000) {
|
||||
copy2(buf, digits2(to_unsigned(year / 100)));
|
||||
copy2(buf, digits2(static_cast<size_t>(year / 100)));
|
||||
} else {
|
||||
offset = 4;
|
||||
write_year_extended(year);
|
||||
|
@ -1387,15 +1388,6 @@ struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
|
|||
FMT_CONSTEXPR void on_duration_unit() {}
|
||||
};
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
inline bool isnan(T) {
|
||||
return false;
|
||||
}
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
inline bool isnan(T value) {
|
||||
return std::isnan(value);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
inline bool isfinite(T) {
|
||||
return true;
|
||||
|
@ -1470,14 +1462,22 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
|||
#endif
|
||||
}
|
||||
|
||||
// Returns the number of fractional digits in the range [0, 18] according to the
|
||||
// Counts the number of fractional digits in the range [0, 18] according to the
|
||||
// C++20 spec. If more than 18 fractional digits are required then returns 6 for
|
||||
// microseconds precision.
|
||||
constexpr int count_fractional_digits(long long num, long long den, int n = 0) {
|
||||
return num % den == 0
|
||||
? n
|
||||
: (n > 18 ? 6 : count_fractional_digits(num * 10, den, n + 1));
|
||||
}
|
||||
template <long long Num, long long Den, int N = 0,
|
||||
bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
|
||||
struct count_fractional_digits {
|
||||
static constexpr int value =
|
||||
Num % Den == 0 ? N : count_fractional_digits<Num * 10, Den, N + 1>::value;
|
||||
};
|
||||
|
||||
// Base case that doesn't instantiate any more templates
|
||||
// in order to avoid overflow.
|
||||
template <long long Num, long long Den, int N>
|
||||
struct count_fractional_digits<Num, Den, N, false> {
|
||||
static constexpr int value = (Num % Den == 0) ? N : 6;
|
||||
};
|
||||
|
||||
constexpr long long pow10(std::uint32_t n) {
|
||||
return n == 0 ? 1 : 10 * pow10(n - 1);
|
||||
|
@ -1663,9 +1663,11 @@ struct chrono_formatter {
|
|||
out = format_decimal<char_type>(out, n, num_digits).end;
|
||||
}
|
||||
|
||||
template <class Duration> void write_fractional_seconds(Duration d) {
|
||||
template <typename Duration> void write_fractional_seconds(Duration d) {
|
||||
FMT_ASSERT(!std::is_floating_point<typename Duration::rep>::value, "");
|
||||
constexpr auto num_fractional_digits =
|
||||
count_fractional_digits(Duration::period::num, Duration::period::den);
|
||||
count_fractional_digits<Duration::period::num,
|
||||
Duration::period::den>::value;
|
||||
|
||||
using subsecond_precision = std::chrono::duration<
|
||||
typename std::common_type<typename Duration::rep,
|
||||
|
@ -1674,12 +1676,9 @@ struct chrono_formatter {
|
|||
if (std::ratio_less<typename subsecond_precision::period,
|
||||
std::chrono::seconds::period>::value) {
|
||||
*out++ = '.';
|
||||
// Don't convert long double to integer seconds to avoid overflow.
|
||||
using sec = conditional_t<
|
||||
std::is_same<typename Duration::rep, long double>::value,
|
||||
std::chrono::duration<long double>, std::chrono::seconds>;
|
||||
auto fractional = detail::abs(d) - std::chrono::duration_cast<sec>(d);
|
||||
const auto subseconds =
|
||||
auto fractional =
|
||||
detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
|
||||
auto subseconds =
|
||||
std::chrono::treat_as_floating_point<
|
||||
typename subsecond_precision::rep>::value
|
||||
? fractional.count()
|
||||
|
@ -1770,8 +1769,22 @@ struct chrono_formatter {
|
|||
if (handle_nan_inf()) return;
|
||||
|
||||
if (ns == numeric_system::standard) {
|
||||
if (std::is_floating_point<rep>::value) {
|
||||
constexpr auto num_fractional_digits =
|
||||
count_fractional_digits<Period::num, Period::den>::value;
|
||||
auto buf = memory_buffer();
|
||||
format_to(std::back_inserter(buf), runtime("{:.{}f}"),
|
||||
std::fmod(val * static_cast<rep>(Period::num) /
|
||||
static_cast<rep>(Period::den),
|
||||
60),
|
||||
num_fractional_digits);
|
||||
if (negative) *out++ = '-';
|
||||
if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
|
||||
out = std::copy(buf.begin(), buf.end(), out);
|
||||
} else {
|
||||
write(second(), 2);
|
||||
write_fractional_seconds(std::chrono::duration<rep, Period>{val});
|
||||
write_fractional_seconds(std::chrono::duration<rep, Period>(val));
|
||||
}
|
||||
return;
|
||||
}
|
||||
auto time = tm();
|
||||
|
|
|
@ -10,13 +10,6 @@
|
|||
|
||||
#include "format.h"
|
||||
|
||||
// __declspec(deprecated) is broken in some MSVC versions.
|
||||
#if FMT_MSC_VER
|
||||
# define FMT_DEPRECATED_NONMSVC
|
||||
#else
|
||||
# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
|
@ -214,17 +207,16 @@ FMT_BEGIN_DETAIL_NAMESPACE
|
|||
|
||||
// color is a struct of either a rgb color or a terminal color.
|
||||
struct color_type {
|
||||
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
|
||||
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
|
||||
value{} {
|
||||
FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
|
||||
FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
|
||||
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) |
|
||||
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||
}
|
||||
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
|
||||
value{} {
|
||||
FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
|
||||
: is_rgb(), value{} {
|
||||
value.term_color = static_cast<uint8_t>(term_color);
|
||||
}
|
||||
bool is_rgb;
|
||||
|
@ -239,10 +231,8 @@ FMT_END_DETAIL_NAMESPACE
|
|||
/** A text style consisting of foreground and background colors and emphasis. */
|
||||
class text_style {
|
||||
public:
|
||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems(em) {}
|
||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
|
||||
: set_foreground_color(), set_background_color(), ems(em) {}
|
||||
|
||||
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
|
@ -273,44 +263,32 @@ class text_style {
|
|||
return lhs |= rhs;
|
||||
}
|
||||
|
||||
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
|
||||
const text_style& rhs) {
|
||||
return and_assign(rhs);
|
||||
}
|
||||
|
||||
FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
|
||||
operator&(text_style lhs, const text_style& 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;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||
FMT_CONSTEXPR bool has_background() const noexcept {
|
||||
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;
|
||||
}
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
return ems;
|
||||
}
|
||||
|
||||
private:
|
||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||
detail::color_type text_color) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems() {
|
||||
detail::color_type text_color) noexcept
|
||||
: set_foreground_color(), set_background_color(), ems() {
|
||||
if (is_foreground) {
|
||||
foreground_color = text_color;
|
||||
set_foreground_color = true;
|
||||
|
@ -320,36 +298,9 @@ class text_style {
|
|||
}
|
||||
}
|
||||
|
||||
// DEPRECATED!
|
||||
FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
set_foreground_color = rhs.set_foreground_color;
|
||||
foreground_color = rhs.foreground_color;
|
||||
} else if (rhs.set_foreground_color) {
|
||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||
FMT_THROW(format_error("can't AND a terminal color"));
|
||||
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||
}
|
||||
friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept;
|
||||
|
||||
if (!set_background_color) {
|
||||
set_background_color = rhs.set_background_color;
|
||||
background_color = rhs.background_color;
|
||||
} else if (rhs.set_background_color) {
|
||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||
FMT_THROW(format_error("can't AND a terminal color"));
|
||||
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||
}
|
||||
|
||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||
static_cast<uint8_t>(rhs.ems));
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
|
||||
FMT_NOEXCEPT;
|
||||
|
||||
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
|
||||
FMT_NOEXCEPT;
|
||||
friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept;
|
||||
|
||||
detail::color_type foreground_color;
|
||||
detail::color_type background_color;
|
||||
|
@ -359,17 +310,16 @@ class text_style {
|
|||
};
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR inline text_style operator|(emphasis lhs,
|
||||
emphasis rhs) FMT_NOEXCEPT {
|
||||
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
|
||||
return text_style(lhs) | rhs;
|
||||
}
|
||||
|
||||
|
@ -377,7 +327,7 @@ FMT_BEGIN_DETAIL_NAMESPACE
|
|||
|
||||
template <typename Char> struct ansi_color_escape {
|
||||
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
|
||||
// sequence.
|
||||
if (!text_color.is_rgb) {
|
||||
|
@ -412,7 +362,7 @@ template <typename Char> struct ansi_color_escape {
|
|||
to_esc(color.b, buffer + 15, 'm');
|
||||
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] = {};
|
||||
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
|
||||
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
|
||||
|
@ -433,10 +383,10 @@ template <typename Char> struct ansi_color_escape {
|
|||
}
|
||||
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_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT {
|
||||
FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
|
||||
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
|
||||
return buffer + std::char_traits<Char>::length(buffer);
|
||||
}
|
||||
|
||||
|
@ -445,59 +395,64 @@ template <typename Char> struct ansi_color_escape {
|
|||
Char buffer[7u + 3u * num_emphases + 1u];
|
||||
|
||||
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[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||
out[2] = static_cast<Char>('0' + c % 10);
|
||||
out[3] = static_cast<Char>(delimiter);
|
||||
}
|
||||
static FMT_CONSTEXPR bool has_emphasis(emphasis em,
|
||||
emphasis mask) FMT_NOEXCEPT {
|
||||
static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
|
||||
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
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;");
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
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;");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
|
||||
std::fputs(chars, stream);
|
||||
template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
|
||||
int result = std::fputs(chars, stream);
|
||||
if (result < 0)
|
||||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
||||
std::fputws(chars, stream);
|
||||
template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
|
||||
int result = 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
|
||||
auto reset_color = string_view("\x1b[0m");
|
||||
buffer.append(reset_color.begin(), reset_color.end());
|
||||
}
|
||||
|
||||
template <typename T> struct styled_arg {
|
||||
const T& value;
|
||||
text_style style;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
void vformat_to(buffer<Char>& buf, const text_style& ts,
|
||||
basic_string_view<Char> format_str,
|
||||
|
@ -528,10 +483,14 @@ template <typename S, typename Char = char_t<S>>
|
|||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
detail::vformat_to(buf, ts, to_string_view(format), args);
|
||||
detail::vformat_to(buf, ts, detail::to_string_view(format), args);
|
||||
if (detail::is_utf8()) {
|
||||
detail::print(f, basic_string_view<Char>(buf.begin(), buf.size()));
|
||||
} else {
|
||||
buf.push_back(Char(0));
|
||||
detail::fputs(buf.data(), f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
|
@ -549,7 +508,7 @@ template <typename S, typename... Args,
|
|||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
vprint(f, ts, format_str,
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -574,7 +533,7 @@ inline std::basic_string<Char> vformat(
|
|||
const text_style& ts, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
detail::vformat_to(buf, ts, to_string_view(format_str), args);
|
||||
detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
|
||||
return fmt::to_string(buf);
|
||||
}
|
||||
|
||||
|
@ -593,8 +552,8 @@ inline std::basic_string<Char> vformat(
|
|||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
return fmt::vformat(ts, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
return fmt::vformat(ts, detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -628,8 +587,62 @@ template <typename OutputIt, typename S, typename... Args,
|
|||
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
|
||||
Args&&... args) ->
|
||||
typename std::enable_if<enable, OutputIt>::type {
|
||||
return vformat_to(out, ts, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
return vformat_to(out, ts, detail::to_string_view(format_str),
|
||||
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::color::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
|
||||
|
|
|
@ -13,45 +13,6 @@
|
|||
FMT_BEGIN_NAMESPACE
|
||||
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>
|
||||
inline counting_iterator copy_str(InputIt begin, InputIt end,
|
||||
counting_iterator it) {
|
||||
|
@ -75,8 +36,7 @@ template <typename OutputIt> class truncating_iterator_base {
|
|||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = void;
|
||||
using reference = void;
|
||||
using _Unchecked_type =
|
||||
truncating_iterator_base; // Mark iterator as checked.
|
||||
FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
|
||||
|
||||
OutputIt base() const { return out_; }
|
||||
size_t count() const { return count_; }
|
||||
|
@ -163,12 +123,12 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
|||
# define FMT_COMPILE(s) FMT_STRING(s)
|
||||
#endif
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
template <typename Char, size_t N,
|
||||
fmt::detail_exported::fixed_string<Char, N> Str>
|
||||
struct udl_compiled_string : compiled_string {
|
||||
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};
|
||||
}
|
||||
};
|
||||
|
@ -377,7 +337,8 @@ template <typename T, typename Char>
|
|||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||
size_t pos, int next_arg_id) {
|
||||
str.remove_prefix(pos);
|
||||
auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id);
|
||||
auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {},
|
||||
next_arg_id);
|
||||
auto f = formatter<T, Char>();
|
||||
auto end = f.parse(ctx);
|
||||
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
|
||||
|
@ -573,10 +534,11 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
|||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||
detail::unknown_format>()) {
|
||||
return format(static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
return fmt::format(
|
||||
static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
std::forward<Args>(args)...);
|
||||
} else {
|
||||
return format(compiled, std::forward<Args>(args)...);
|
||||
return fmt::format(compiled, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,11 +548,11 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
|||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||
detail::unknown_format>()) {
|
||||
return format_to(out,
|
||||
static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
return fmt::format_to(
|
||||
out, static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
std::forward<Args>(args)...);
|
||||
} else {
|
||||
return format_to(out, compiled, std::forward<Args>(args)...);
|
||||
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -599,22 +561,23 @@ template <typename OutputIt, typename S, typename... Args,
|
|||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||
const S& format_str, Args&&... args) {
|
||||
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str,
|
||||
std::forward<Args>(args)...);
|
||||
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
|
||||
format_str, std::forward<Args>(args)...);
|
||||
return {it.base(), it.count()};
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
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,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
void print(std::FILE* f, const S& format_str, const Args&... args) {
|
||||
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()});
|
||||
}
|
||||
|
||||
|
@ -624,14 +587,12 @@ void print(const S& format_str, const Args&... args) {
|
|||
print(stdout, format_str, args...);
|
||||
}
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
inline namespace literals {
|
||||
template <detail_exported::fixed_string Str>
|
||||
constexpr detail::udl_compiled_string<
|
||||
remove_cvref_t<decltype(Str.data[0])>,
|
||||
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str>
|
||||
operator""_cf() {
|
||||
return {};
|
||||
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
|
||||
using char_t = remove_cvref_t<decltype(Str.data[0])>;
|
||||
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
|
||||
Str>();
|
||||
}
|
||||
} // namespace literals
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1838
include/fmt/format.h
1838
include/fmt/format.h
File diff suppressed because it is too large
Load diff
|
@ -1,2 +0,0 @@
|
|||
#include "xchar.h"
|
||||
#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
|
101
include/fmt/os.h
101
include/fmt/os.h
|
@ -9,10 +9,8 @@
|
|||
#define FMT_OS_H_
|
||||
|
||||
#include <cerrno>
|
||||
#include <clocale> // locale_t
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib> // strtod_l
|
||||
#include <system_error> // std::system_error
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
|
@ -141,7 +139,7 @@ template <typename Char> struct formatter<std::error_code, Char> {
|
|||
};
|
||||
|
||||
#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
|
||||
// A converter from UTF-16 to UTF-8.
|
||||
|
@ -165,7 +163,7 @@ class utf16_to_utf8 {
|
|||
};
|
||||
|
||||
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_API std::system_error vwindows_error(int error_code, string_view format_str,
|
||||
|
@ -207,10 +205,9 @@ std::system_error windows_error(int error_code, string_view message,
|
|||
|
||||
// Reports a Windows error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
FMT_API void report_windows_error(int error_code,
|
||||
const char* message) FMT_NOEXCEPT;
|
||||
FMT_API void report_windows_error(int error_code, const char* message) noexcept;
|
||||
#else
|
||||
inline const std::error_category& system_category() FMT_NOEXCEPT {
|
||||
inline const std::error_category& system_category() noexcept {
|
||||
return std::system_category();
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
@ -237,13 +234,13 @@ class buffered_file {
|
|||
void operator=(const buffered_file&) = delete;
|
||||
|
||||
// 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.
|
||||
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||
FMT_API ~buffered_file() noexcept;
|
||||
|
||||
public:
|
||||
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
|
||||
buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
|
||||
other.file_ = nullptr;
|
||||
}
|
||||
|
||||
|
@ -261,11 +258,9 @@ class buffered_file {
|
|||
FMT_API void close();
|
||||
|
||||
// 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
|
||||
// of MinGW that define fileno as a macro.
|
||||
FMT_API int(fileno)() const;
|
||||
FMT_API int descriptor() const;
|
||||
|
||||
void vprint(string_view format_str, format_args args) {
|
||||
fmt::vprint(file_, format_str, args);
|
||||
|
@ -279,12 +274,12 @@ class buffered_file {
|
|||
|
||||
#if FMT_USE_FCNTL
|
||||
// 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
|
||||
// closing the file multiple times will cause a crash on Windows rather
|
||||
// than an exception. You can get standard behavior by overriding the
|
||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||
class file {
|
||||
class FMT_API file {
|
||||
private:
|
||||
int fd_; // File descriptor.
|
||||
|
||||
|
@ -303,16 +298,16 @@ class 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.
|
||||
FMT_API file(cstring_view path, int oflag);
|
||||
file(cstring_view path, int oflag);
|
||||
|
||||
public:
|
||||
file(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.
|
||||
file& operator=(file&& other) {
|
||||
|
@ -323,43 +318,43 @@ class file {
|
|||
}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~file() FMT_NOEXCEPT;
|
||||
~file() noexcept;
|
||||
|
||||
// Returns the file descriptor.
|
||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||
int descriptor() const noexcept { return fd_; }
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
void close();
|
||||
|
||||
// Returns the file size. The size has signed type for consistency with
|
||||
// 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.
|
||||
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.
|
||||
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
|
||||
// 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
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd);
|
||||
void dup2(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// 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
|
||||
// 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
|
||||
// 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.
|
||||
|
@ -462,7 +457,7 @@ class FMT_API ostream final : private detail::buffer<char> {
|
|||
|
||||
* ``<integer>``: Flags passed to `open
|
||||
<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
|
||||
|
||||
**Example**::
|
||||
|
@ -477,50 +472,6 @@ inline ostream output_file(cstring_view path, T... params) {
|
|||
}
|
||||
#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.
|
||||
FMT_DEPRECATED 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_END_NAMESPACE
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#ifndef FMT_OSTREAM_H_
|
||||
#define FMT_OSTREAM_H_
|
||||
|
||||
#include <fstream>
|
||||
#include <ostream>
|
||||
|
||||
#include "format.h"
|
||||
|
@ -45,15 +46,59 @@ struct is_streamable<
|
|||
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_same<T, std::basic_string<Char>>::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 {};
|
||||
|
||||
template <typename Char> FILE* get_file(std::basic_filebuf<Char>&) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct dummy_filebuf {
|
||||
FILE* _Myfile;
|
||||
};
|
||||
template <typename T, typename U = int> struct ms_filebuf {
|
||||
using type = dummy_filebuf;
|
||||
};
|
||||
template <typename T> struct ms_filebuf<T, decltype(T::_Myfile, 0)> {
|
||||
using type = T;
|
||||
};
|
||||
using filebuf_type = ms_filebuf<std::filebuf>::type;
|
||||
|
||||
FILE* get_file(filebuf_type& buf);
|
||||
|
||||
// Generate a unique explicit instantion in every translation unit using a tag
|
||||
// type in an anonymous namespace.
|
||||
namespace {
|
||||
struct filebuf_access_tag {};
|
||||
} // namespace
|
||||
template <typename Tag, typename FileMemberPtr, FileMemberPtr file>
|
||||
class filebuf_access {
|
||||
friend FILE* get_file(filebuf_type& buf) { return buf.*file; }
|
||||
};
|
||||
template class filebuf_access<filebuf_access_tag,
|
||||
decltype(&filebuf_type::_Myfile),
|
||||
&filebuf_type::_Myfile>;
|
||||
|
||||
inline bool write(std::filebuf& buf, fmt::string_view data) {
|
||||
FILE* f = get_file(buf);
|
||||
if (!f) return false;
|
||||
print(f, data);
|
||||
return true;
|
||||
}
|
||||
inline bool write(std::wfilebuf&, fmt::basic_string_view<wchar_t>) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the content of buf to os.
|
||||
// It is a separate function rather than a part of vprint to simplify testing.
|
||||
template <typename Char>
|
||||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
if (const_check(FMT_MSC_VERSION)) {
|
||||
auto filebuf = dynamic_cast<std::basic_filebuf<Char>*>(os.rdbuf());
|
||||
if (filebuf && write(*filebuf, {buf.data(), buf.size()})) return;
|
||||
}
|
||||
const Char* buf_data = buf.data();
|
||||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||
unsigned_streamsize size = buf.size();
|
||||
|
@ -76,38 +121,65 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||
#endif
|
||||
output << value;
|
||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
buf.try_resize(buf.size());
|
||||
}
|
||||
|
||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||
template <typename T, typename Char>
|
||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||
: private formatter<basic_string_view<Char>, Char> {
|
||||
using formatter<basic_string_view<Char>, Char>::parse;
|
||||
template <typename T> struct streamed_view { const T& value; };
|
||||
|
||||
template <typename OutputIt>
|
||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
||||
} // 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);
|
||||
}
|
||||
};
|
||||
|
||||
// DEPRECATED!
|
||||
using ostream_formatter = basic_ostream_formatter<char>;
|
||||
|
||||
template <typename T>
|
||||
struct formatter<detail::streamed_view<T>> : ostream_formatter {
|
||||
template <typename OutputIt>
|
||||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
||||
-> OutputIt {
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
format_value(buffer, value, ctx.locale());
|
||||
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
||||
auto format(detail::streamed_view<T> view,
|
||||
basic_format_context<OutputIt, char>& ctx) const -> OutputIt {
|
||||
return ostream_formatter::format(view.value, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns a view that formats `value` via an ostream ``operator<<``.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print("Current thread id: {}\n",
|
||||
fmt::streamed(std::this_thread::get_id()));
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
auto streamed(const T& value) -> detail::streamed_view<T> {
|
||||
return {value};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||
template <typename T, typename Char>
|
||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||
: basic_ostream_formatter<Char> {
|
||||
using basic_ostream_formatter<Char>::format;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename Char>
|
||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||
FMT_MODULE_EXPORT template <typename Char>
|
||||
void vprint(std::basic_ostream<Char>& os,
|
||||
basic_string_view<type_identity_t<Char>> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
|
@ -123,13 +195,19 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
|||
fmt::print(cerr, "Don't {}!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||
vprint(os, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
FMT_MODULE_EXPORT template <typename... T>
|
||||
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
||||
vprint(os, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename... Args>
|
||||
void print(std::wostream& os,
|
||||
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
|
||||
Args&&... args) {
|
||||
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OSTREAM_H_
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
#include <algorithm> // std::max
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <ostream>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
|
@ -561,7 +560,7 @@ inline auto vsprintf(
|
|||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
vprintf(buffer, detail::to_string_view(fmt), args);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
|
@ -578,7 +577,8 @@ template <typename S, typename... T,
|
|||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...));
|
||||
return vsprintf(detail::to_string_view(fmt),
|
||||
fmt::make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
|
@ -587,7 +587,7 @@ inline auto vfprintf(
|
|||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
vprintf(buffer, detail::to_string_view(fmt), args);
|
||||
size_t size = buffer.size();
|
||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||
? -1
|
||||
|
@ -606,7 +606,7 @@ inline auto vfprintf(
|
|||
template <typename S, typename... T, typename Char = char_t<S>>
|
||||
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vfprintf(f, to_string_view(fmt),
|
||||
return vfprintf(f, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
|
@ -615,7 +615,7 @@ inline auto vprintf(
|
|||
const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
return vfprintf(stdout, to_string_view(fmt), args);
|
||||
return vfprintf(stdout, detail::to_string_view(fmt), args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -630,27 +630,10 @@ inline auto vprintf(
|
|||
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
inline auto printf(const S& fmt, const T&... args) -> int {
|
||||
return vprintf(
|
||||
to_string_view(fmt),
|
||||
detail::to_string_view(fmt),
|
||||
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
FMT_DEPRECATED auto vfprintf(
|
||||
std::basic_ostream<Char>& os, const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
|
||||
return static_cast<int>(buffer.size());
|
||||
}
|
||||
template <typename S, typename... T, typename Char = char_t<S>>
|
||||
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
|
||||
const T&... args) -> int {
|
||||
return vfprintf(os, to_string_view(fmt),
|
||||
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ template <typename T> class is_std_string_like {
|
|||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
static constexpr const bool value =
|
||||
is_string<T>::value ||
|
||||
std::is_convertible<T, std_string_view<char>>::value ||
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
|
@ -70,9 +70,9 @@ template <typename T> class is_map {
|
|||
|
||||
public:
|
||||
#ifdef FMT_FORMAT_MAP_AS_LIST
|
||||
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||
static constexpr const bool value = false;
|
||||
#else
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
static constexpr const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
#endif
|
||||
};
|
||||
|
@ -83,9 +83,9 @@ template <typename T> class is_set {
|
|||
|
||||
public:
|
||||
#ifdef FMT_FORMAT_SET_AS_LIST
|
||||
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||
static constexpr const bool value = false;
|
||||
#else
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
static constexpr const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
|
||||
#endif
|
||||
};
|
||||
|
@ -94,7 +94,7 @@ template <typename... Ts> struct conditional_helper {};
|
|||
|
||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
|
||||
|
||||
# define FMT_DECLTYPE_RETURN(val) \
|
||||
->decltype(val) { return val; } \
|
||||
|
@ -174,12 +174,12 @@ template <typename T> class is_tuple_like_ {
|
|||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
static constexpr const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
// Check for integer_sequence
|
||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
|
||||
template <typename T, T... N>
|
||||
using integer_sequence = std::integer_sequence<T, N...>;
|
||||
template <size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||
|
@ -202,8 +202,33 @@ template <size_t N>
|
|||
using make_index_sequence = make_integer_sequence<size_t, N>;
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
|
||||
|
||||
template <typename T, typename C, bool = is_tuple_like_<T>::value>
|
||||
class is_tuple_formattable_ {
|
||||
public:
|
||||
static constexpr const bool value = false;
|
||||
};
|
||||
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
|
||||
template <std::size_t... I>
|
||||
static std::true_type check2(index_sequence<I...>,
|
||||
integer_sequence<bool, (I == I)...>);
|
||||
static std::false_type check2(...);
|
||||
template <std::size_t... I>
|
||||
static decltype(check2(
|
||||
index_sequence<I...>{},
|
||||
integer_sequence<
|
||||
bool, (is_formattable<typename std::tuple_element<I, T>::type,
|
||||
C>::value)...>{})) check(index_sequence<I...>);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
decltype(check(tuple_index_sequence<T>{}))::value;
|
||||
};
|
||||
|
||||
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 free function get<I>(T) now.
|
||||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||
|
@ -221,9 +246,36 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
|||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||
}
|
||||
|
||||
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
|
||||
// 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>
|
||||
using value_type =
|
||||
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>;
|
||||
using range_reference_type =
|
||||
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 Range>
|
||||
using uncvref_first_type = remove_cvref_t<
|
||||
decltype(std::declval<range_reference_type<Range>>().first)>;
|
||||
|
||||
template <typename Range>
|
||||
using uncvref_second_type = remove_cvref_t<
|
||||
decltype(std::declval<range_reference_type<Range>>().second)>;
|
||||
|
||||
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
||||
*out++ = ',';
|
||||
|
@ -231,286 +283,9 @@ template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
|||
return out;
|
||||
}
|
||||
|
||||
struct singleton {
|
||||
unsigned char upper;
|
||||
unsigned char lower_count;
|
||||
};
|
||||
|
||||
inline auto is_printable(uint16_t x, const singleton* singletons,
|
||||
size_t singletons_size,
|
||||
const unsigned char* singleton_lowers,
|
||||
const unsigned char* normal, size_t normal_size)
|
||||
-> bool {
|
||||
auto upper = x >> 8;
|
||||
auto lower_start = 0;
|
||||
for (size_t i = 0; i < singletons_size; ++i) {
|
||||
auto s = singletons[i];
|
||||
auto lower_end = lower_start + s.lower_count;
|
||||
if (upper < s.upper) break;
|
||||
if (upper == s.upper) {
|
||||
for (auto j = lower_start; j < lower_end; ++j) {
|
||||
if (singleton_lowers[j] == (x & 0xff)) return false;
|
||||
}
|
||||
}
|
||||
lower_start = lower_end;
|
||||
}
|
||||
|
||||
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 singletons0[] = {
|
||||
{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 singletons0_lower[] = {
|
||||
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 singletons1[] = {
|
||||
{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 singletons1_lower[] = {
|
||||
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 is_printable(lower, singletons0,
|
||||
sizeof(singletons0) / sizeof(*singletons0),
|
||||
singletons0_lower, normal0, sizeof(normal0));
|
||||
}
|
||||
if (cp < 0x20000) {
|
||||
return is_printable(lower, singletons1,
|
||||
sizeof(singletons1) / sizeof(*singletons1),
|
||||
singletons1_lower, 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 cp < 0x110000;
|
||||
}
|
||||
|
||||
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 (sizeof(Char) == 1 && cp >= 0x80) continue;
|
||||
if (needs_escape(cp)) return {begin, begin + 1, cp};
|
||||
}
|
||||
return {begin, nullptr, 0};
|
||||
}
|
||||
|
||||
inline 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>
|
||||
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
|
||||
*out++ = '"';
|
||||
auto begin = str.begin(), end = str.end();
|
||||
do {
|
||||
auto escape = find_escape(begin, end);
|
||||
out = copy_str<Char>(begin, escape.begin, out);
|
||||
begin = escape.end;
|
||||
if (!begin) break;
|
||||
auto c = static_cast<Char>(escape.cp);
|
||||
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()) {
|
||||
if (escape.cp < 0x100) {
|
||||
out = format_to(out, "\\x{:02x}", escape.cp);
|
||||
continue;
|
||||
}
|
||||
if (escape.cp < 0x10000) {
|
||||
out = format_to(out, "\\u{:04x}", escape.cp);
|
||||
continue;
|
||||
}
|
||||
if (escape.cp < 0x110000) {
|
||||
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;
|
||||
return write_escaped_string(out, str);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
|
@ -523,10 +298,7 @@ inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
|
|||
template <typename Char, typename OutputIt, typename Arg,
|
||||
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
|
||||
OutputIt write_range_entry(OutputIt out, const Arg v) {
|
||||
*out++ = '\'';
|
||||
*out++ = v;
|
||||
*out++ = '\'';
|
||||
return out;
|
||||
return write_escaped_char(out, v);
|
||||
}
|
||||
|
||||
template <
|
||||
|
@ -540,12 +312,19 @@ OutputIt write_range_entry(OutputIt out, const Arg& v) {
|
|||
} // namespace detail
|
||||
|
||||
template <typename T> struct is_tuple_like {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
static constexpr const bool value =
|
||||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename C> struct is_tuple_formattable {
|
||||
static constexpr const bool value =
|
||||
detail::is_tuple_formattable_<T, C>::value;
|
||||
};
|
||||
|
||||
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 &&
|
||||
fmt::is_tuple_formattable<TupleT, Char>::value>> {
|
||||
private:
|
||||
// C++11 generic lambda for format().
|
||||
template <typename FormatContext> struct format_each {
|
||||
|
@ -565,7 +344,8 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
|||
}
|
||||
|
||||
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();
|
||||
*out++ = '(';
|
||||
detail::for_each(values, format_each<FormatContext>{0, out});
|
||||
|
@ -575,50 +355,101 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
|||
};
|
||||
|
||||
template <typename T, typename Char> struct is_range {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
static constexpr const bool 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_constructible<detail::std_string_view<Char>, T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
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<
|
||||
T, Char,
|
||||
R, Char,
|
||||
enable_if_t<
|
||||
fmt::is_range<T, Char>::value
|
||||
// Workaround a bug in MSVC 2019 and earlier.
|
||||
#if !FMT_MSC_VER
|
||||
&& (is_formattable<detail::value_type<T>, Char>::value ||
|
||||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
||||
conjunction<fmt::is_range<R, Char>
|
||||
// Workaround a bug in MSVC 2017 and earlier.
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920
|
||||
,
|
||||
disjunction<
|
||||
is_formattable<detail::uncvref_type<detail::maybe_const_range<R>>,
|
||||
Char>,
|
||||
detail::has_fallback_formatter<
|
||||
detail::uncvref_type<detail::maybe_const_range<R>>, Char>
|
||||
>
|
||||
#endif
|
||||
>::value
|
||||
>> {
|
||||
|
||||
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()) {
|
||||
return 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, typename U,
|
||||
FMT_ENABLE_IF(
|
||||
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
||||
const T, T>>::value)>
|
||||
auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
#ifdef FMT_DEPRECATED_BRACED_RANGES
|
||||
Char prefix = '{';
|
||||
Char postfix = '}';
|
||||
#else
|
||||
Char prefix = detail::is_set<T>::value ? '{' : '[';
|
||||
Char postfix = detail::is_set<T>::value ? '}' : ']';
|
||||
#endif
|
||||
template <typename FormatContext>
|
||||
auto format(range_type& range, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
Char prefix = detail::is_set<R>::value ? '{' : '[';
|
||||
Char postfix = detail::is_set<R>::value ? '}' : ']';
|
||||
detail::range_mapper<buffer_context<Char>> mapper;
|
||||
auto out = ctx.out();
|
||||
*out++ = prefix;
|
||||
int i = 0;
|
||||
auto it = std::begin(range);
|
||||
auto end = std::end(range);
|
||||
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;
|
||||
|
@ -629,13 +460,20 @@ struct formatter<
|
|||
template <typename T, typename Char>
|
||||
struct formatter<
|
||||
T, Char,
|
||||
enable_if_t<
|
||||
detail::is_map<T>::value
|
||||
// Workaround a bug in MSVC 2019 and earlier.
|
||||
#if !FMT_MSC_VER
|
||||
&& (is_formattable<detail::value_type<T>, Char>::value ||
|
||||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
||||
enable_if_t<conjunction<detail::is_map<T>
|
||||
// Workaround a bug in MSVC 2017 and earlier.
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920
|
||||
,
|
||||
disjunction<
|
||||
is_formattable<detail::uncvref_first_type<T>, Char>,
|
||||
detail::has_fallback_formatter<detail::uncvref_first_type<T>, Char>
|
||||
>,
|
||||
disjunction<
|
||||
is_formattable<detail::uncvref_second_type<T>, Char>,
|
||||
detail::has_fallback_formatter<detail::uncvref_second_type<T>, Char>
|
||||
>
|
||||
#endif
|
||||
>::value
|
||||
>> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
|
@ -647,7 +485,7 @@ struct formatter<
|
|||
FMT_ENABLE_IF(
|
||||
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
||||
const T, T>>::value)>
|
||||
auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto format(U& map, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
*out++ = '{';
|
||||
int i = 0;
|
||||
|
|
176
include/fmt/std.h
Normal file
176
include/fmt/std.h
Normal file
|
@ -0,0 +1,176 @@
|
|||
// Formatting library for C++ - formatters for standard library types
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_STD_H_
|
||||
#define FMT_STD_H_
|
||||
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "ostream.h"
|
||||
|
||||
#if FMT_HAS_INCLUDE(<version>)
|
||||
# include <version>
|
||||
#endif
|
||||
// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
|
||||
#if FMT_CPLUSPLUS >= 201703L
|
||||
# if FMT_HAS_INCLUDE(<filesystem>)
|
||||
# include <filesystem>
|
||||
# endif
|
||||
# if FMT_HAS_INCLUDE(<variant>)
|
||||
# include <variant>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __cpp_lib_filesystem
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Char>
|
||||
void write_escaped_path(basic_memory_buffer<Char>& quoted,
|
||||
const std::filesystem::path& p) {
|
||||
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
|
||||
}
|
||||
# ifdef _WIN32
|
||||
template <>
|
||||
inline void write_escaped_path<char>(basic_memory_buffer<char>& quoted,
|
||||
const std::filesystem::path& p) {
|
||||
auto s = p.u8string();
|
||||
write_escaped_string<char>(
|
||||
std::back_inserter(quoted),
|
||||
string_view(reinterpret_cast<const char*>(s.c_str()), s.size()));
|
||||
}
|
||||
# endif
|
||||
template <>
|
||||
inline void write_escaped_path<std::filesystem::path::value_type>(
|
||||
basic_memory_buffer<std::filesystem::path::value_type>& quoted,
|
||||
const std::filesystem::path& p) {
|
||||
write_escaped_string<std::filesystem::path::value_type>(
|
||||
std::back_inserter(quoted), p.native());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920
|
||||
// For MSVC 2017 and earlier using the partial specialization
|
||||
// would cause an ambiguity error, therefore we provide it only
|
||||
// conditionally.
|
||||
template <typename Char>
|
||||
struct formatter<std::filesystem::path, Char>
|
||||
: formatter<basic_string_view<Char>> {
|
||||
template <typename FormatContext>
|
||||
auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
|
||||
typename FormatContext::iterator {
|
||||
basic_memory_buffer<Char> quoted;
|
||||
detail::write_escaped_path(quoted, p);
|
||||
return formatter<basic_string_view<Char>>::format(
|
||||
basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
FMT_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <typename Char>
|
||||
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#ifdef __cpp_lib_variant
|
||||
FMT_BEGIN_NAMESPACE
|
||||
template <typename Char> struct formatter<std::monostate, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const std::monostate&, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
out = detail::write<Char>(out, "monostate");
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
using variant_index_sequence =
|
||||
std::make_index_sequence<std::variant_size<T>::value>;
|
||||
|
||||
// variant_size and variant_alternative check.
|
||||
template <typename T, typename U = void>
|
||||
struct is_variant_like_ : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>>
|
||||
: std::true_type {};
|
||||
|
||||
// formattable element check
|
||||
template <typename T, typename C> class is_variant_formattable_ {
|
||||
template <std::size_t... I>
|
||||
static std::conjunction<
|
||||
is_formattable<std::variant_alternative_t<I, T>, C>...>
|
||||
check(std::index_sequence<I...>);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
decltype(check(variant_index_sequence<T>{}))::value;
|
||||
};
|
||||
|
||||
template <typename Char, typename OutputIt, typename T>
|
||||
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
|
||||
if constexpr (is_string<T>::value)
|
||||
return write_escaped_string<Char>(out, detail::to_string_view(v));
|
||||
else if constexpr (std::is_same_v<T, Char>)
|
||||
return write_escaped_char(out, v);
|
||||
else
|
||||
return write<Char>(out, v);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T> struct is_variant_like {
|
||||
static constexpr const bool value = detail::is_variant_like_<T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename C> struct is_variant_formattable {
|
||||
static constexpr const bool value =
|
||||
detail::is_variant_formattable_<T, C>::value;
|
||||
};
|
||||
|
||||
template <typename Variant, typename Char>
|
||||
struct formatter<
|
||||
Variant, Char,
|
||||
std::enable_if_t<std::conjunction_v<
|
||||
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const Variant& value, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
|
||||
out = detail::write<Char>(out, "variant(");
|
||||
std::visit(
|
||||
[&](const auto& v) {
|
||||
out = detail::write_variant_alternative<Char>(out, v);
|
||||
},
|
||||
value);
|
||||
*out++ = ')';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
#endif // FMT_STD_H_
|
|
@ -47,12 +47,7 @@ constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
|
|||
}
|
||||
|
||||
inline namespace literals {
|
||||
constexpr auto operator"" _format(const wchar_t* s, size_t n)
|
||||
-> detail::udl_formatter<wchar_t> {
|
||||
return {{s, n}};
|
||||
}
|
||||
|
||||
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
|
||||
return {s};
|
||||
}
|
||||
|
@ -87,13 +82,23 @@ auto vformat(basic_string_view<Char> format_str,
|
|||
return to_string(buffer);
|
||||
}
|
||||
|
||||
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409
|
||||
template <typename... Args>
|
||||
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
|
||||
#endif
|
||||
|
||||
template <typename... T>
|
||||
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
|
||||
return vformat(fmt, fmt::make_wformat_args(args...));
|
||||
}
|
||||
|
||||
// Pass char_t as a default template parameter instead of using
|
||||
// std::basic_string<char_t<S>> to reduce the symbol size.
|
||||
template <typename S, typename... Args, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||
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), vargs);
|
||||
return vformat(detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename Char = char_t<S>,
|
||||
|
@ -103,7 +108,7 @@ inline auto vformat(
|
|||
const Locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
return detail::vformat(loc, to_string_view(format_str), args);
|
||||
return detail::vformat(loc, detail::to_string_view(format_str), args);
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename... Args,
|
||||
|
@ -112,8 +117,8 @@ template <typename Locale, typename S, typename... Args,
|
|||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto format(const Locale& loc, const S& format_str, Args&&... args)
|
||||
-> std::basic_string<Char> {
|
||||
return detail::vformat(loc, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
return detail::vformat(loc, detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
||||
|
@ -123,7 +128,7 @@ auto vformat_to(OutputIt out, const S& format_str,
|
|||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> OutputIt {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
detail::vformat_to(buf, to_string_view(format_str), args);
|
||||
detail::vformat_to(buf, detail::to_string_view(format_str), args);
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
|
@ -132,18 +137,8 @@ template <typename OutputIt, typename S, typename... Args,
|
|||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
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), vargs);
|
||||
}
|
||||
|
||||
template <typename S, typename... Args, typename Char, size_t SIZE,
|
||||
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
|
||||
const S& format_str, Args&&... args) ->
|
||||
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), vargs, {});
|
||||
return detail::buffer_appender<Char>(buf);
|
||||
return vformat_to(out, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename OutputIt, typename... Args,
|
||||
|
@ -155,7 +150,8 @@ inline auto vformat_to(
|
|||
OutputIt out, const Locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
|
||||
vformat_to(buf, detail::to_string_view(format_str), args,
|
||||
detail::locale_ref(loc));
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
|
@ -167,8 +163,8 @@ template <
|
|||
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
|
||||
Args&&... args) ->
|
||||
typename std::enable_if<enable, OutputIt>::type {
|
||||
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||
return vformat_to(out, loc, to_string_view(format_str), vargs);
|
||||
return vformat_to(out, loc, to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char, typename... Args,
|
||||
|
@ -190,16 +186,16 @@ template <typename OutputIt, typename S, typename... Args,
|
|||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
|
||||
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), vargs);
|
||||
return vformat_to_n(out, n, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename... Args, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
|
||||
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
|
||||
detail::counting_buffer<Char> buf;
|
||||
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
|
||||
detail::vformat_to(buf, to_string_view(fmt), vargs);
|
||||
detail::vformat_to(buf, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
return buf.count();
|
||||
}
|
||||
|
||||
|
|
109
src/format.cc
109
src/format.cc
|
@ -10,115 +10,38 @@
|
|||
FMT_BEGIN_NAMESPACE
|
||||
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>
|
||||
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||
T value) {
|
||||
#ifdef FMT_FUZZ
|
||||
if (precision > 100000)
|
||||
throw std::runtime_error(
|
||||
"fuzz mode - avoid large allocation inside snprintf");
|
||||
#endif
|
||||
// Suppress the warning about nonliteral format string.
|
||||
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
|
||||
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||
: snprintf_ptr(buf, size, format, precision, value);
|
||||
}
|
||||
|
||||
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
|
||||
FMT_NOEXCEPT;
|
||||
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
|
||||
FMT_NOEXCEPT;
|
||||
} // namespace detail
|
||||
|
||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||
int (*instantiate_format_float)(double, int, detail::float_specs,
|
||||
detail::buffer<char>&) = detail::format_float;
|
||||
template FMT_API auto dragonbox::to_decimal(float x) noexcept
|
||||
-> dragonbox::decimal_fp<float>;
|
||||
template FMT_API auto dragonbox::to_decimal(double x) noexcept
|
||||
-> dragonbox::decimal_fp<double>;
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
|
||||
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
|
||||
template FMT_API locale_ref::locale_ref(const std::locale& loc);
|
||||
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
|
||||
#endif
|
||||
|
||||
// Explicit instantiations for char.
|
||||
|
||||
template FMT_API auto detail::thousands_sep_impl(locale_ref)
|
||||
template FMT_API auto thousands_sep_impl(locale_ref)
|
||||
-> thousands_sep_result<char>;
|
||||
template FMT_API char detail::decimal_point_impl(locale_ref);
|
||||
template FMT_API auto decimal_point_impl(locale_ref) -> char;
|
||||
|
||||
template FMT_API void detail::buffer<char>::append(const char*, const char*);
|
||||
template FMT_API void buffer<char>::append(const char*, const char*);
|
||||
|
||||
// DEPRECATED!
|
||||
// There is no correspondent extern template in format.h because of
|
||||
// incompatibility between clang and gcc (#2377).
|
||||
template FMT_API void detail::vformat_to(
|
||||
detail::buffer<char>&, string_view,
|
||||
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
|
||||
|
||||
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
|
||||
detail::buffer<char>&);
|
||||
template FMT_API int detail::snprintf_float(long double, int,
|
||||
detail::float_specs,
|
||||
detail::buffer<char>&);
|
||||
template FMT_API int detail::format_float(double, int, detail::float_specs,
|
||||
detail::buffer<char>&);
|
||||
template FMT_API int detail::format_float(long double, int, detail::float_specs,
|
||||
detail::buffer<char>&);
|
||||
template FMT_API void vformat_to(buffer<char>&, string_view,
|
||||
basic_format_args<FMT_BUFFER_CONTEXT(char)>,
|
||||
locale_ref);
|
||||
|
||||
// Explicit instantiations for wchar_t.
|
||||
|
||||
template FMT_API auto detail::thousands_sep_impl(locale_ref)
|
||||
template FMT_API auto thousands_sep_impl(locale_ref)
|
||||
-> thousands_sep_result<wchar_t>;
|
||||
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
|
||||
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
|
||||
|
||||
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
|
||||
const wchar_t*);
|
||||
|
||||
template struct detail::basic_data<void>;
|
||||
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
|
||||
|
||||
} // namespace detail
|
||||
FMT_END_NAMESPACE
|
||||
|
|
42
src/os.cc
42
src/os.cc
|
@ -35,9 +35,15 @@
|
|||
# ifndef S_IRGRP
|
||||
# define S_IRGRP 0
|
||||
# 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 // _WIN32
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
|
@ -45,10 +51,6 @@
|
|||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef fileno
|
||||
# undef fileno
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#ifdef _WIN32
|
||||
// Return type of read and write functions.
|
||||
|
@ -107,7 +109,7 @@ class system_message {
|
|||
unsigned long result_;
|
||||
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';
|
||||
}
|
||||
|
||||
|
@ -126,15 +128,15 @@ class system_message {
|
|||
}
|
||||
}
|
||||
~system_message() { LocalFree(message_); }
|
||||
explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; }
|
||||
operator basic_string_view<wchar_t>() const FMT_NOEXCEPT {
|
||||
explicit operator bool() const noexcept { return result_ != 0; }
|
||||
operator basic_string_view<wchar_t>() const noexcept {
|
||||
return basic_string_view<wchar_t>(message_, result_);
|
||||
}
|
||||
};
|
||||
|
||||
class utf8_system_category final : public std::error_category {
|
||||
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 {
|
||||
system_message msg(error_code);
|
||||
if (msg) {
|
||||
|
@ -149,7 +151,7 @@ class utf8_system_category final : public std::error_category {
|
|||
|
||||
} // 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;
|
||||
return category;
|
||||
}
|
||||
|
@ -161,13 +163,13 @@ std::system_error vwindows_error(int err_code, string_view format_str,
|
|||
}
|
||||
|
||||
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
||||
const char* message) FMT_NOEXCEPT {
|
||||
const char* message) noexcept {
|
||||
FMT_TRY {
|
||||
system_message msg(error_code);
|
||||
if (msg) {
|
||||
utf16_to_utf8 utf8_message;
|
||||
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
|
||||
format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
|
||||
fmt::format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -176,12 +178,12 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
|||
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);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
buffered_file::~buffered_file() FMT_NOEXCEPT {
|
||||
buffered_file::~buffered_file() noexcept {
|
||||
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
||||
report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
@ -200,11 +202,8 @@ void buffered_file::close() {
|
|||
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
||||
#define FMT_ARGS
|
||||
|
||||
int buffered_file::fileno() const {
|
||||
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
||||
int buffered_file::descriptor() const {
|
||||
int fd = FMT_POSIX_CALL(fileno(file_));
|
||||
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
|
||||
return fd;
|
||||
}
|
||||
|
@ -214,7 +213,8 @@ file::file(cstring_view path, int oflag) {
|
|||
# ifdef _WIN32
|
||||
using mode_t = int;
|
||||
# endif
|
||||
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
constexpr mode_t mode =
|
||||
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
fd_ = -1;
|
||||
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
||||
|
@ -225,7 +225,7 @@ file::file(cstring_view path, int oflag) {
|
|||
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!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
||||
|
@ -299,7 +299,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;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1) ec = std::error_code(errno, std::generic_category());
|
||||
|
|
|
@ -1 +1 @@
|
|||
4.2.1
|
||||
5.1.1
|
||||
|
|
|
@ -11,18 +11,17 @@ cc_library(
|
|||
"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/format.h",
|
||||
"include/fmt/os.h",
|
||||
"include/fmt/ostream.h",
|
||||
"include/fmt/printf.h",
|
||||
"include/fmt/ranges.h",
|
||||
"include/fmt/std.h",
|
||||
"include/fmt/xchar.h",
|
||||
],
|
||||
includes = [
|
||||
"include",
|
||||
"src",
|
||||
],
|
||||
strip_include_prefix = "include",
|
||||
visibility = ["//visibility:public"],
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
# C++14 feature support detection
|
||||
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CheckCXXCompilerFlag)
|
||||
function (fmt_check_cxx_compiler_flag flag result)
|
||||
if (NOT MSVC)
|
||||
check_cxx_compiler_flag("${flag}" ${result})
|
||||
endif ()
|
||||
endfunction ()
|
||||
|
||||
if (NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
@ -9,35 +13,38 @@ endif()
|
|||
message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}")
|
||||
|
||||
if (CMAKE_CXX_STANDARD EQUAL 20)
|
||||
check_cxx_compiler_flag(-std=c++20 has_std_20_flag)
|
||||
check_cxx_compiler_flag(-std=c++2a has_std_2a_flag)
|
||||
fmt_check_cxx_compiler_flag(-std=c++20 has_std_20_flag)
|
||||
fmt_check_cxx_compiler_flag(-std=c++2a has_std_2a_flag)
|
||||
|
||||
if (has_std_20_flag)
|
||||
set(CXX_STANDARD_FLAG -std=c++20)
|
||||
elseif (has_std_2a_flag)
|
||||
set(CXX_STANDARD_FLAG -std=c++2a)
|
||||
endif ()
|
||||
|
||||
elseif (CMAKE_CXX_STANDARD EQUAL 17)
|
||||
check_cxx_compiler_flag(-std=c++17 has_std_17_flag)
|
||||
check_cxx_compiler_flag(-std=c++1z has_std_1z_flag)
|
||||
fmt_check_cxx_compiler_flag(-std=c++17 has_std_17_flag)
|
||||
fmt_check_cxx_compiler_flag(-std=c++1z has_std_1z_flag)
|
||||
|
||||
if (has_std_17_flag)
|
||||
set(CXX_STANDARD_FLAG -std=c++17)
|
||||
elseif (has_std_1z_flag)
|
||||
set(CXX_STANDARD_FLAG -std=c++1z)
|
||||
endif ()
|
||||
|
||||
elseif (CMAKE_CXX_STANDARD EQUAL 14)
|
||||
check_cxx_compiler_flag(-std=c++14 has_std_14_flag)
|
||||
check_cxx_compiler_flag(-std=c++1y has_std_1y_flag)
|
||||
fmt_check_cxx_compiler_flag(-std=c++14 has_std_14_flag)
|
||||
fmt_check_cxx_compiler_flag(-std=c++1y has_std_1y_flag)
|
||||
|
||||
if (has_std_14_flag)
|
||||
set(CXX_STANDARD_FLAG -std=c++14)
|
||||
elseif (has_std_1y_flag)
|
||||
set(CXX_STANDARD_FLAG -std=c++1y)
|
||||
endif ()
|
||||
|
||||
elseif (CMAKE_CXX_STANDARD EQUAL 11)
|
||||
check_cxx_compiler_flag(-std=c++11 has_std_11_flag)
|
||||
check_cxx_compiler_flag(-std=c++0x has_std_0x_flag)
|
||||
fmt_check_cxx_compiler_flag(-std=c++11 has_std_11_flag)
|
||||
fmt_check_cxx_compiler_flag(-std=c++0x has_std_0x_flag)
|
||||
|
||||
if (has_std_11_flag)
|
||||
set(CXX_STANDARD_FLAG -std=c++11)
|
||||
|
@ -45,26 +52,3 @@ elseif (CMAKE_CXX_STANDARD EQUAL 11)
|
|||
set(CXX_STANDARD_FLAG -std=c++0x)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
|
||||
|
||||
# Check if user-defined literals are available
|
||||
check_cxx_source_compiles("
|
||||
void operator\"\" _udl(long double);
|
||||
int main() {}"
|
||||
SUPPORTS_USER_DEFINED_LITERALS)
|
||||
if (NOT SUPPORTS_USER_DEFINED_LITERALS)
|
||||
set (SUPPORTS_USER_DEFINED_LITERALS OFF)
|
||||
endif ()
|
||||
|
||||
# Check if <variant> is available
|
||||
set(CMAKE_REQUIRED_FLAGS -std=c++1z)
|
||||
check_cxx_source_compiles("
|
||||
#include <variant>
|
||||
int main() {}"
|
||||
FMT_HAS_VARIANT)
|
||||
if (NOT FMT_HAS_VARIANT)
|
||||
set (FMT_HAS_VARIANT OFF)
|
||||
endif ()
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS )
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
if (NOT TARGET fmt::fmt)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake)
|
||||
endif ()
|
||||
|
||||
check_required_components(fmt)
|
||||
|
|
|
@ -171,7 +171,7 @@ def main():
|
|||
normal1 = compress_normal(normal1)
|
||||
|
||||
print("""\
|
||||
inline auto is_printable(uint32_t cp) -> bool {\
|
||||
FMT_FUNC auto is_printable(uint32_t cp) -> bool {\
|
||||
""")
|
||||
print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower')
|
||||
print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower')
|
||||
|
|
|
@ -4,9 +4,7 @@ set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc)
|
|||
add_library(test-main STATIC ${TEST_MAIN_SRC})
|
||||
target_include_directories(test-main PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
|
||||
target_link_libraries(test-main gtest)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
target_link_libraries(test-main gtest fmt)
|
||||
|
||||
function(add_fmt_executable name)
|
||||
add_executable(${name} ${ARGN})
|
||||
|
@ -79,6 +77,15 @@ endif()
|
|||
add_fmt_test(printf-test)
|
||||
add_fmt_test(ranges-test ranges-odr-test.cc)
|
||||
add_fmt_test(scan-test)
|
||||
add_fmt_test(std-test)
|
||||
try_compile(compile_result_unused
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
SOURCES ${CMAKE_CURRENT_LIST_DIR}/detect-stdfs.cc
|
||||
OUTPUT_VARIABLE RAWOUTPUT)
|
||||
string(REGEX REPLACE ".*libfound \"([^\"]*)\".*" "\\1" STDLIBFS "${RAWOUTPUT}")
|
||||
if (STDLIBFS)
|
||||
target_link_libraries(std-test ${STDLIBFS})
|
||||
endif ()
|
||||
add_fmt_test(unicode-test HEADER_ONLY)
|
||||
if (MSVC)
|
||||
target_compile_options(unicode-test PRIVATE /utf-8)
|
||||
|
@ -128,9 +135,6 @@ if (NOT MSVC_STATIC_RUNTIME)
|
|||
if (FMT_PEDANTIC)
|
||||
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
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_fmt_test(os-test)
|
||||
endif ()
|
||||
|
@ -148,10 +152,8 @@ if (FMT_PEDANTIC)
|
|||
target_include_directories(
|
||||
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
target_compile_options(noexception-test PRIVATE -fno-exceptions)
|
||||
if (FMT_PEDANTIC)
|
||||
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Test that the library compiles without locale.
|
||||
add_library(nolocale-test ../src/format.cc)
|
||||
|
@ -174,8 +176,8 @@ if (FMT_PEDANTIC AND NOT WIN32)
|
|||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
|
||||
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
|
||||
"-DFMT_DIR=${CMAKE_SOURCE_DIR}"
|
||||
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
|
||||
"-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}"
|
||||
"-DFMT_DIR=${CMAKE_SOURCE_DIR}")
|
||||
|
||||
# Test if the targets are found from the build directory.
|
||||
add_test(find-package-test ${CMAKE_CTEST_COMMAND}
|
||||
|
|
|
@ -557,6 +557,9 @@ TEST(chrono_test, special_durations) {
|
|||
"03:33");
|
||||
EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}),
|
||||
"03:33:20");
|
||||
EXPECT_EQ("44.000000000000",
|
||||
fmt::format("{:%S}", std::chrono::duration<float, std::pico>(
|
||||
1.54213895E+26)));
|
||||
}
|
||||
|
||||
TEST(chrono_test, unsigned_duration) {
|
||||
|
@ -620,6 +623,10 @@ TEST(chrono_test, cpp20_duration_subsecond_support) {
|
|||
// 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
|
||||
|
|
|
@ -50,6 +50,12 @@ TEST(color_test, format) {
|
|||
"\x1b[105mtbmagenta\x1b[0m");
|
||||
EXPECT_EQ(fmt::format(fg(fmt::terminal_color::red), "{}", "foo"),
|
||||
"\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) {
|
||||
|
|
|
@ -6,6 +6,8 @@ project(compile-error-test CXX)
|
|||
set(fmt_headers "
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/xchar.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <iostream>
|
||||
")
|
||||
|
||||
set(error_test_names "")
|
||||
|
@ -154,6 +156,28 @@ expect_compile(format-function-error "
|
|||
fmt::format(\"{}\", f);
|
||||
" ERROR)
|
||||
|
||||
# Formatting an unformattable argument should always be a compile time error
|
||||
expect_compile(format-lots-of-arguments-with-unformattable "
|
||||
struct E {};
|
||||
fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, E());
|
||||
" ERROR)
|
||||
expect_compile(format-lots-of-arguments-with-function "
|
||||
void (*f)();
|
||||
fmt::format(\"\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, f);
|
||||
" ERROR)
|
||||
|
||||
# Check if user-defined literals are available
|
||||
include(CheckCXXSourceCompiles)
|
||||
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
|
||||
check_cxx_source_compiles("
|
||||
void operator\"\" _udl(long double);
|
||||
int main() {}"
|
||||
SUPPORTS_USER_DEFINED_LITERALS)
|
||||
set(CMAKE_REQUIRED_FLAGS )
|
||||
if (NOT SUPPORTS_USER_DEFINED_LITERALS)
|
||||
set (SUPPORTS_USER_DEFINED_LITERALS OFF)
|
||||
endif ()
|
||||
|
||||
# Make sure that compiler features detected in the header
|
||||
# match the features detected in CMake.
|
||||
if (SUPPORTS_USER_DEFINED_LITERALS)
|
||||
|
@ -181,16 +205,30 @@ if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
|
|||
#error
|
||||
#endif
|
||||
" ERROR)
|
||||
expect_compile(print-string-number-spec-error "
|
||||
#ifdef FMT_HAS_CONSTEVAL
|
||||
fmt::print(\"{:d}\", \"I am not a number\");
|
||||
#else
|
||||
#error
|
||||
#endif
|
||||
" ERROR)
|
||||
expect_compile(print-stream-string-number-spec-error "
|
||||
#ifdef FMT_HAS_CONSTEVAL
|
||||
fmt::print(std::cout, \"{: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
|
||||
#if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
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
|
||||
#if defined(FMT_HAS_CONSTEVAL) && FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
using namespace fmt::literals;
|
||||
fmt::print(\"{foo}\", \"bar\"_a=42);
|
||||
#else
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#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
|
||||
__cpp_constexpr_dynamic_alloc >= 201907 && FMT_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;
|
||||
|
|
|
@ -187,7 +187,7 @@ TEST(compile_test, named) {
|
|||
EXPECT_THROW(fmt::format(FMT_COMPILE("{invalid}"), fmt::arg("valid", 42)),
|
||||
fmt::format_error);
|
||||
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
using namespace fmt::literals;
|
||||
auto statically_named_field_compiled =
|
||||
fmt::detail::compile<decltype("arg"_a = 42)>(FMT_COMPILE("{arg}"));
|
||||
|
@ -201,6 +201,11 @@ TEST(compile_test, named) {
|
|||
# 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) {
|
||||
char buf[8];
|
||||
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);
|
||||
|
@ -280,7 +285,7 @@ TEST(compile_test, print) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
TEST(compile_test, compile_format_string_literal) {
|
||||
using namespace fmt::literals;
|
||||
EXPECT_EQ("", fmt::format(""_cf));
|
||||
|
@ -289,8 +294,15 @@ TEST(compile_test, compile_format_string_literal) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 202002L || \
|
||||
(__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002)
|
||||
// MSVS 2019 19.29.30145.0 - Support C++20 and OK.
|
||||
// MSVS 2022 19.32.31332.0 - compile-test.cc(362,3): fatal error C1001: Internal
|
||||
// compiler error.
|
||||
// (compiler file
|
||||
// 'D:\a\_work\1\s\src\vctools\Compiler\CxxFE\sl\p1\c\constexpr\constexpr.cpp',
|
||||
// line 8635)
|
||||
#if ((FMT_CPLUSPLUS >= 202002L) && \
|
||||
(!FMT_MSC_VERSION || FMT_MSC_VERSION < 1930)) || \
|
||||
(FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
|
||||
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;
|
||||
|
|
|
@ -128,7 +128,7 @@ TEST(core_test, buffer_appender) {
|
|||
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
|
||||
TEST(buffer_test, noncopyable) {
|
||||
EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value);
|
||||
# if !FMT_MSC_VER
|
||||
# if !FMT_MSC_VERSION
|
||||
// std::is_copy_assignable is broken in MSVC2013.
|
||||
EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
|
||||
# endif
|
||||
|
@ -136,7 +136,7 @@ TEST(buffer_test, noncopyable) {
|
|||
|
||||
TEST(buffer_test, nonmoveable) {
|
||||
EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value);
|
||||
# if !FMT_MSC_VER
|
||||
# if !FMT_MSC_VERSION
|
||||
// std::is_move_assignable is broken in MSVC2013.
|
||||
EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
|
||||
# endif
|
||||
|
@ -385,11 +385,11 @@ VISIT_TYPE(unsigned long, unsigned long long);
|
|||
|
||||
template <typename T> class numeric_arg_test : public testing::Test {};
|
||||
|
||||
using types =
|
||||
using test_types =
|
||||
testing::Types<bool, signed char, unsigned char, short, unsigned short, int,
|
||||
unsigned, long, unsigned long, long long, unsigned long long,
|
||||
float, double, long double>;
|
||||
TYPED_TEST_SUITE(numeric_arg_test, types);
|
||||
TYPED_TEST_SUITE(numeric_arg_test, test_types);
|
||||
|
||||
template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0>
|
||||
T test_value() {
|
||||
|
@ -679,6 +679,7 @@ TEST(format_test, constexpr_parse_format_string) {
|
|||
#endif // FMT_USE_CONSTEXPR
|
||||
|
||||
struct enabled_formatter {};
|
||||
struct enabled_ptr_formatter {};
|
||||
struct disabled_formatter {};
|
||||
struct disabled_formatter_convertible {
|
||||
operator int() const { return 42; }
|
||||
|
@ -693,6 +694,16 @@ template <> struct formatter<enabled_formatter> {
|
|||
return ctx.out();
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct formatter<enabled_ptr_formatter*> {
|
||||
auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
auto format(enabled_ptr_formatter*, format_context& ctx)
|
||||
-> decltype(ctx.out()) {
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
TEST(core_test, has_formatter) {
|
||||
|
@ -737,7 +748,34 @@ struct convertible_to_pointer {
|
|||
operator const int*() const { return nullptr; }
|
||||
};
|
||||
|
||||
enum class test_scoped_enum {};
|
||||
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) {
|
||||
#if 0
|
||||
|
@ -758,6 +796,7 @@ TEST(core_test, is_formattable) {
|
|||
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_ptr_formatter*>::value, "");
|
||||
static_assert(!fmt::is_formattable<disabled_formatter>::value, "");
|
||||
static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, "");
|
||||
|
||||
|
@ -765,19 +804,22 @@ TEST(core_test, is_formattable) {
|
|||
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
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 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<test_scoped_enum>::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"); }
|
||||
|
@ -788,6 +830,10 @@ TEST(core_test, format_to) {
|
|||
EXPECT_EQ(s, "42");
|
||||
}
|
||||
|
||||
TEST(core_test, format_as) {
|
||||
EXPECT_EQ(fmt::format("{}", test::formattable_scoped_enum()), "42");
|
||||
}
|
||||
|
||||
struct convertible_to_int {
|
||||
operator int() const { return 42; }
|
||||
};
|
||||
|
@ -850,13 +896,16 @@ TEST(core_test, format_implicitly_convertible_to_string_view) {
|
|||
}
|
||||
|
||||
// std::is_constructible is broken in MSVC until version 2015.
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900
|
||||
struct explicitly_convertible_to_string_view {
|
||||
explicit operator fmt::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
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
|
||||
|
@ -865,8 +914,11 @@ struct explicitly_convertible_to_std_string_view {
|
|||
};
|
||||
|
||||
TEST(core_test, format_explicitly_convertible_to_std_string_view) {
|
||||
EXPECT_EQ("foo",
|
||||
fmt::format("{}", explicitly_convertible_to_std_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_std_string_view>::value,
|
||||
"");
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
|
18
test/detect-stdfs.cc
Normal file
18
test/detect-stdfs.cc
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Formatting library for C++ - tests of formatters for standard library types
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <exception> // _GLIBCXX_RELEASE & _LIBCPP_VERSION
|
||||
|
||||
#if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 8
|
||||
# error libfound "stdc++fs"
|
||||
#elif !defined(__apple_build_version__) && defined(_LIBCPP_VERSION) && \
|
||||
_LIBCPP_VERSION >= 7000 && _LIBCPP_VERSION < 9000
|
||||
# error libfound "c++fs"
|
||||
#else
|
||||
// none if std::filesystem does not require additional libraries
|
||||
# error libfound ""
|
||||
#endif
|
|
@ -24,9 +24,9 @@ static_assert(!std::is_copy_constructible<bigint>::value, "");
|
|||
static_assert(!std::is_copy_assignable<bigint>::value, "");
|
||||
|
||||
TEST(bigint_test, construct) {
|
||||
EXPECT_EQ("", fmt::format("{}", bigint()));
|
||||
EXPECT_EQ("42", fmt::format("{}", bigint(0x42)));
|
||||
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
|
||||
EXPECT_EQ(fmt::to_string(bigint()), "");
|
||||
EXPECT_EQ(fmt::to_string(bigint(0x42)), "42");
|
||||
EXPECT_EQ(fmt::to_string(bigint(0x123456789abcedf0)), "123456789abcedf0");
|
||||
}
|
||||
|
||||
TEST(bigint_test, compare) {
|
||||
|
@ -72,63 +72,56 @@ TEST(bigint_test, add_compare) {
|
|||
TEST(bigint_test, shift_left) {
|
||||
bigint n(0x42);
|
||||
n <<= 0;
|
||||
EXPECT_EQ("42", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "42");
|
||||
n <<= 1;
|
||||
EXPECT_EQ("84", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "84");
|
||||
n <<= 25;
|
||||
EXPECT_EQ("108000000", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "108000000");
|
||||
}
|
||||
|
||||
TEST(bigint_test, multiply) {
|
||||
bigint n(0x42);
|
||||
EXPECT_THROW(n *= 0, assertion_failure);
|
||||
n *= 1;
|
||||
EXPECT_EQ("42", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "42");
|
||||
|
||||
n *= 2;
|
||||
EXPECT_EQ("84", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "84");
|
||||
n *= 0x12345678;
|
||||
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
|
||||
EXPECT_EQ(fmt::to_string(n), "962fc95e0");
|
||||
|
||||
bigint bigmax(max_value<uint32_t>());
|
||||
bigmax *= max_value<uint32_t>();
|
||||
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
|
||||
bigmax.assign(max_value<uint64_t>());
|
||||
bigmax *= max_value<uint64_t>();
|
||||
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
|
||||
}
|
||||
EXPECT_EQ(fmt::to_string(bigmax), "fffffffe00000001");
|
||||
|
||||
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);
|
||||
const auto max64 = max_value<uint64_t>();
|
||||
bigmax = max64;
|
||||
bigmax *= max64;
|
||||
EXPECT_EQ(fmt::to_string(bigmax), "fffffffffffffffe0000000000000001");
|
||||
|
||||
const auto max128 = (fmt::detail::uint128_t(max64) << 64) | max64;
|
||||
bigmax = max128;
|
||||
bigmax *= max128;
|
||||
EXPECT_EQ(fmt::to_string(bigmax),
|
||||
"fffffffffffffffffffffffffffffffe00000000000000000000000000000001");
|
||||
}
|
||||
|
||||
TEST(bigint_test, square) {
|
||||
bigint n0(0);
|
||||
n0.square();
|
||||
EXPECT_EQ("0", fmt::format("{}", n0));
|
||||
EXPECT_EQ(fmt::to_string(n0), "0");
|
||||
bigint n1(0x100);
|
||||
n1.square();
|
||||
EXPECT_EQ("10000", fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1), "10000");
|
||||
bigint n2(0xfffffffff);
|
||||
n2.square();
|
||||
EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2));
|
||||
EXPECT_EQ(fmt::to_string(n2), "ffffffffe000000001");
|
||||
bigint n3(max_value<uint64_t>());
|
||||
n3.square();
|
||||
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3));
|
||||
EXPECT_EQ(fmt::to_string(n3), "fffffffffffffffe0000000000000001");
|
||||
bigint n4;
|
||||
n4.assign_pow10(10);
|
||||
EXPECT_EQ("2540be400", fmt::format("{}", n4));
|
||||
EXPECT_EQ(fmt::to_string(n4), "2540be400");
|
||||
}
|
||||
|
||||
TEST(bigint_test, divmod_assign_zero_divisor) {
|
||||
|
@ -150,8 +143,8 @@ TEST(bigint_test, divmod_assign_unaligned) {
|
|||
n2.assign_pow10(100);
|
||||
int result = n1.divmod_assign(n2);
|
||||
EXPECT_EQ(result, 9406);
|
||||
EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96",
|
||||
fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1),
|
||||
"10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96");
|
||||
}
|
||||
|
||||
TEST(bigint_test, divmod_assign) {
|
||||
|
@ -159,19 +152,19 @@ TEST(bigint_test, divmod_assign) {
|
|||
bigint n1(100);
|
||||
int result = n1.divmod_assign(bigint(10));
|
||||
EXPECT_EQ(result, 10);
|
||||
EXPECT_EQ("0", fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1), "0");
|
||||
// pow(10, 100) / (42 << 320):
|
||||
n1.assign_pow10(100);
|
||||
result = n1.divmod_assign(bigint(42) <<= 320);
|
||||
EXPECT_EQ(result, 111);
|
||||
EXPECT_EQ("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96",
|
||||
fmt::format("{}", n1));
|
||||
EXPECT_EQ(fmt::to_string(n1),
|
||||
"13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96");
|
||||
// 42 / 100:
|
||||
bigint n2(42);
|
||||
n1.assign_pow10(2);
|
||||
result = n2.divmod_assign(n1);
|
||||
EXPECT_EQ(result, 0);
|
||||
EXPECT_EQ("2a", fmt::format("{}", n2));
|
||||
EXPECT_EQ(fmt::to_string(n2), "2a");
|
||||
}
|
||||
|
||||
template <bool is_iec559> void run_double_tests() {
|
||||
|
@ -190,8 +183,8 @@ TEST(fp_test, double_tests) {
|
|||
TEST(fp_test, normalize) {
|
||||
const auto v = fp(0xbeef, 42);
|
||||
auto normalized = normalize(v);
|
||||
EXPECT_EQ(0xbeef000000000000, normalized.f);
|
||||
EXPECT_EQ(-6, normalized.e);
|
||||
EXPECT_EQ(normalized.f, 0xbeef000000000000);
|
||||
EXPECT_EQ(normalized.e, -6);
|
||||
}
|
||||
|
||||
TEST(fp_test, multiply) {
|
||||
|
@ -207,18 +200,18 @@ TEST(fp_test, get_cached_power) {
|
|||
using limits = std::numeric_limits<double>;
|
||||
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
|
||||
int dec_exp = 0;
|
||||
auto fp = fmt::detail::get_cached_power(exp, dec_exp);
|
||||
bigint exact, cache(fp.f);
|
||||
auto power = fmt::detail::get_cached_power(exp, dec_exp);
|
||||
bigint exact, cache(power.f);
|
||||
if (dec_exp >= 0) {
|
||||
exact.assign_pow10(dec_exp);
|
||||
if (fp.e <= 0)
|
||||
exact <<= -fp.e;
|
||||
if (power.e <= 0)
|
||||
exact <<= -power.e;
|
||||
else
|
||||
cache <<= fp.e;
|
||||
cache <<= power.e;
|
||||
exact.align(cache);
|
||||
cache.align(exact);
|
||||
auto exact_str = fmt::format("{}", exact);
|
||||
auto cache_str = fmt::format("{}", cache);
|
||||
auto exact_str = fmt::to_string(exact);
|
||||
auto cache_str = fmt::to_string(cache);
|
||||
EXPECT_EQ(exact_str.size(), cache_str.size());
|
||||
EXPECT_EQ(exact_str.substr(0, 15), cache_str.substr(0, 15));
|
||||
int diff = cache_str[15] - exact_str[15];
|
||||
|
@ -228,12 +221,12 @@ TEST(fp_test, get_cached_power) {
|
|||
EXPECT_EQ(diff, 0);
|
||||
} else {
|
||||
cache.assign_pow10(-dec_exp);
|
||||
cache *= fp.f + 1; // Inexact check.
|
||||
exact.assign(1);
|
||||
exact <<= -fp.e;
|
||||
cache *= power.f + 1; // Inexact check.
|
||||
exact = 1;
|
||||
exact <<= -power.e;
|
||||
exact.align(cache);
|
||||
auto exact_str = fmt::format("{}", exact);
|
||||
auto cache_str = fmt::format("{}", cache);
|
||||
auto exact_str = fmt::to_string(exact);
|
||||
auto cache_str = fmt::to_string(cache);
|
||||
EXPECT_EQ(exact_str.size(), cache_str.size());
|
||||
EXPECT_EQ(exact_str.substr(0, 16), cache_str.substr(0, 16));
|
||||
}
|
||||
|
@ -243,38 +236,41 @@ TEST(fp_test, get_cached_power) {
|
|||
TEST(fp_test, dragonbox_max_k) {
|
||||
using fmt::detail::dragonbox::floor_log10_pow2;
|
||||
using float_info = fmt::detail::dragonbox::float_info<float>;
|
||||
EXPECT_EQ(fmt::detail::const_check(float_info::max_k),
|
||||
float_info::kappa - floor_log10_pow2(float_info::min_exponent -
|
||||
float_info::significand_bits));
|
||||
EXPECT_EQ(
|
||||
fmt::detail::const_check(float_info::max_k),
|
||||
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>;
|
||||
EXPECT_EQ(
|
||||
fmt::detail::const_check(double_info::max_k),
|
||||
double_info::kappa - floor_log10_pow2(double_info::min_exponent -
|
||||
double_info::significand_bits));
|
||||
double_info::kappa -
|
||||
floor_log10_pow2(std::numeric_limits<double>::min_exponent -
|
||||
fmt::detail::num_significand_bits<double>() - 1));
|
||||
}
|
||||
|
||||
TEST(fp_test, get_round_direction) {
|
||||
using fmt::detail::get_round_direction;
|
||||
using fmt::detail::round_direction;
|
||||
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0));
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0));
|
||||
EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10));
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(100, 60, 10));
|
||||
EXPECT_EQ(get_round_direction(100, 50, 0), round_direction::down);
|
||||
EXPECT_EQ(get_round_direction(100, 51, 0), round_direction::up);
|
||||
EXPECT_EQ(get_round_direction(100, 40, 10), round_direction::down);
|
||||
EXPECT_EQ(get_round_direction(100, 60, 10), round_direction::up);
|
||||
for (size_t i = 41; i < 60; ++i)
|
||||
EXPECT_EQ(round_direction::unknown, get_round_direction(100, i, 10));
|
||||
EXPECT_EQ(get_round_direction(100, i, 10), round_direction::unknown);
|
||||
uint64_t max = max_value<uint64_t>();
|
||||
EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure);
|
||||
EXPECT_THROW(get_round_direction(100, 0, 100), assertion_failure);
|
||||
EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure);
|
||||
// Check that remainder + error doesn't overflow.
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 2));
|
||||
EXPECT_EQ(get_round_direction(max, max - 1, 2), round_direction::up);
|
||||
// Check that 2 * (remainder + error) doesn't overflow.
|
||||
EXPECT_EQ(round_direction::unknown,
|
||||
get_round_direction(max, max / 2 + 1, max / 2));
|
||||
EXPECT_EQ(get_round_direction(max, max / 2 + 1, max / 2),
|
||||
round_direction::unknown);
|
||||
// Check that remainder - error doesn't overflow.
|
||||
EXPECT_EQ(round_direction::unknown, get_round_direction(100, 40, 41));
|
||||
EXPECT_EQ(get_round_direction(100, 40, 41), round_direction::unknown);
|
||||
// Check that 2 * (remainder - error) doesn't overflow.
|
||||
EXPECT_EQ(round_direction::up, get_round_direction(max, max - 1, 1));
|
||||
EXPECT_EQ(get_round_direction(max, max - 1, 1), round_direction::up);
|
||||
}
|
||||
|
||||
TEST(fp_test, fixed_handler) {
|
||||
|
@ -297,20 +293,20 @@ TEST(fp_test, fixed_handler) {
|
|||
}
|
||||
|
||||
TEST(fp_test, grisu_format_compiles_with_on_ieee_double) {
|
||||
fmt::memory_buffer buf;
|
||||
auto buf = fmt::memory_buffer();
|
||||
format_float(0.42, -1, fmt::detail::float_specs(), buf);
|
||||
}
|
||||
|
||||
TEST(format_impl_test, format_error_code) {
|
||||
std::string msg = "error 42", sep = ": ";
|
||||
{
|
||||
fmt::memory_buffer buffer;
|
||||
auto buffer = fmt::memory_buffer();
|
||||
format_to(fmt::appender(buffer), "garbage");
|
||||
fmt::detail::format_error_code(buffer, 42, "test");
|
||||
EXPECT_EQ("test: " + msg, to_string(buffer));
|
||||
EXPECT_EQ(to_string(buffer), "test: " + msg);
|
||||
}
|
||||
{
|
||||
fmt::memory_buffer buffer;
|
||||
auto buffer = fmt::memory_buffer();
|
||||
auto prefix =
|
||||
std::string(fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x');
|
||||
fmt::detail::format_error_code(buffer, 42, prefix);
|
||||
|
@ -331,7 +327,7 @@ TEST(format_impl_test, format_error_code) {
|
|||
// Test with a message that doesn't fit into the buffer.
|
||||
prefix += 'x';
|
||||
fmt::detail::format_error_code(buffer, codes[i], prefix);
|
||||
EXPECT_EQ(msg, to_string(buffer));
|
||||
EXPECT_EQ(to_string(buffer), msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -347,8 +343,8 @@ template <typename Int> void test_count_digits() {
|
|||
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i));
|
||||
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
|
||||
n *= 10;
|
||||
EXPECT_EQ(i, fmt::detail::count_digits(n - 1));
|
||||
EXPECT_EQ(i + 1, fmt::detail::count_digits(n));
|
||||
EXPECT_EQ(fmt::detail::count_digits(n - 1), i);
|
||||
EXPECT_EQ(fmt::detail::count_digits(n), i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -357,21 +353,51 @@ TEST(format_impl_test, count_digits) {
|
|||
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");
|
||||
#if FMT_USE_FLOAT128
|
||||
TEST(format_impl_test, write_float128) {
|
||||
auto s = std::string();
|
||||
fmt::detail::write<char>(std::back_inserter(s), __float128(42));
|
||||
EXPECT_EQ(s, "42");
|
||||
}
|
||||
#endif
|
||||
|
||||
struct double_double {
|
||||
double a;
|
||||
double b;
|
||||
|
||||
explicit constexpr double_double(double a_val = 0, double b_val = 0)
|
||||
: a(a_val), b(b_val) {}
|
||||
|
||||
operator double() const { return a + b; }
|
||||
auto operator-() const -> double_double { return double_double(-a, -b); }
|
||||
};
|
||||
|
||||
bool operator>=(const double_double& lhs, const double_double& rhs) {
|
||||
return lhs.a + lhs.b >= rhs.a + rhs.b;
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template <> struct is_floating_point<double_double> : std::true_type {};
|
||||
template <> struct numeric_limits<double_double> {
|
||||
// is_iec559 is true for double-double in libstdc++.
|
||||
static constexpr bool is_iec559 = true;
|
||||
static constexpr int digits = 106;
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
TEST(format_impl_test, write_double_double) {
|
||||
auto s = std::string();
|
||||
fmt::detail::write<char>(std::back_inserter(s), double_double(42), {});
|
||||
#ifndef _MSC_VER // MSVC has an issue with specializing is_floating_point.
|
||||
EXPECT_EQ(s, "42");
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
TEST(format_impl_test, write_console_signature) {
|
||||
decltype(WriteConsoleW)* p = fmt::detail::WriteConsoleW;
|
||||
decltype(::WriteConsoleW)* p = fmt::detail::WriteConsoleW;
|
||||
(void)p;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -33,12 +33,98 @@ using fmt::memory_buffer;
|
|||
using fmt::runtime;
|
||||
using fmt::string_view;
|
||||
using fmt::detail::max_value;
|
||||
using fmt::detail::uint128_fallback;
|
||||
|
||||
using testing::Return;
|
||||
using testing::StrictMock;
|
||||
|
||||
enum { buffer_size = 256 };
|
||||
|
||||
TEST(uint128_test, ctor) {
|
||||
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 = 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 = uint128_fallback(42);
|
||||
EXPECT_EQ(n - 2, 40);
|
||||
}
|
||||
|
||||
TEST(uint128_test, plus_assign) {
|
||||
auto n = uint128_fallback(32);
|
||||
n += uint128_fallback(10);
|
||||
EXPECT_EQ(n, 42);
|
||||
n = uint128_fallback(max_value<uint64_t>());
|
||||
n += uint128_fallback(1);
|
||||
EXPECT_EQ(n, uint128_fallback(1) << 64);
|
||||
}
|
||||
|
||||
TEST(uint128_test, multiply) {
|
||||
auto n = uint128_fallback(2251799813685247);
|
||||
n = n * 3611864890;
|
||||
EXPECT_EQ(static_cast<uint64_t>(n >> 64), 440901);
|
||||
}
|
||||
|
||||
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>;
|
||||
FMT_CONSTEXPR20 auto result = isfinite(Float(limits::infinity()));
|
||||
EXPECT_FALSE(result);
|
||||
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 {
|
||||
uint32_t u[2];
|
||||
};
|
||||
|
@ -223,8 +309,9 @@ TEST(memory_buffer_test, move_ctor_dynamic_buffer) {
|
|||
buffer.push_back('a');
|
||||
basic_memory_buffer<char, 4, std_allocator> buffer2(std::move(buffer));
|
||||
// Move should rip the guts of the first buffer.
|
||||
EXPECT_EQ(inline_buffer_ptr, &buffer[0]);
|
||||
EXPECT_EQ("testa", std::string(&buffer2[0], buffer2.size()));
|
||||
EXPECT_EQ(&buffer[0], inline_buffer_ptr);
|
||||
EXPECT_EQ(buffer.size(), 0);
|
||||
EXPECT_EQ(std::string(&buffer2[0], buffer2.size()), "testa");
|
||||
EXPECT_GT(buffer2.capacity(), 4u);
|
||||
}
|
||||
|
||||
|
@ -325,7 +412,7 @@ template <typename Allocator, size_t MaxSize>
|
|||
class max_size_allocator : public Allocator {
|
||||
public:
|
||||
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) {
|
||||
if (n > max_size()) {
|
||||
throw std::length_error("size > max_size");
|
||||
|
@ -570,6 +657,9 @@ TEST(format_test, plus_sign) {
|
|||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error,
|
||||
"format specifier requires signed argument");
|
||||
EXPECT_EQ("+42", fmt::format("{0:+}", 42ll));
|
||||
#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");
|
||||
EXPECT_EQ("+42", fmt::format("{0:+}", 42.0));
|
||||
|
@ -918,6 +1008,14 @@ TEST(format_test, precision) {
|
|||
EXPECT_THAT(outputs,
|
||||
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(
|
||||
fmt::format("{:0g}", std::numeric_limits<long double>::denorm_min()),
|
||||
"3.6452e-4951");
|
||||
}
|
||||
|
||||
EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0));
|
||||
EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234));
|
||||
EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001));
|
||||
|
@ -939,6 +1037,7 @@ TEST(format_test, precision) {
|
|||
format_error, "number is too big");
|
||||
|
||||
EXPECT_EQ("st", fmt::format("{0:.2}", "str"));
|
||||
EXPECT_EQ("вожык", fmt::format("{0:.5}", "вожыкі"));
|
||||
}
|
||||
|
||||
TEST(format_test, runtime_precision) {
|
||||
|
@ -1060,7 +1159,7 @@ TEST(format_test, format_short) {
|
|||
template <typename T>
|
||||
void check_unknown_types(const T& value, const char* types, const char*) {
|
||||
char format_str[buffer_size];
|
||||
const char* special = ".0123456789L}";
|
||||
const char* special = ".0123456789L?}";
|
||||
for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) {
|
||||
char c = static_cast<char>(i);
|
||||
if (std::strchr(types, c) || std::strchr(special, c) || !c) continue;
|
||||
|
@ -1229,32 +1328,30 @@ TEST(format_test, format_float) {
|
|||
}
|
||||
|
||||
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");
|
||||
EXPECT_EQ("0", fmt::format("{:}", 0.0));
|
||||
EXPECT_EQ("0.000000", fmt::format("{:f}", 0.0));
|
||||
EXPECT_EQ("0", fmt::format("{:g}", 0.0));
|
||||
EXPECT_EQ("392.65", fmt::format("{:}", 392.65));
|
||||
EXPECT_EQ("392.65", fmt::format("{:g}", 392.65));
|
||||
EXPECT_EQ("392.65", fmt::format("{:G}", 392.65));
|
||||
EXPECT_EQ("4.9014e+06", fmt::format("{:g}", 4.9014e6));
|
||||
EXPECT_EQ("392.650000", fmt::format("{:f}", 392.65));
|
||||
EXPECT_EQ("392.650000", fmt::format("{:F}", 392.65));
|
||||
EXPECT_EQ("42", fmt::format("{:L}", 42.0));
|
||||
EXPECT_EQ(" 0x1.0cccccccccccdp+2", fmt::format("{:24a}", 4.2));
|
||||
EXPECT_EQ("0x1.0cccccccccccdp+2 ", fmt::format("{:<24a}", 4.2));
|
||||
EXPECT_EQ(fmt::format("{:}", 0.0), "0");
|
||||
EXPECT_EQ(fmt::format("{:f}", 0.0), "0.000000");
|
||||
EXPECT_EQ(fmt::format("{:g}", 0.0), "0");
|
||||
EXPECT_EQ(fmt::format("{:}", 392.65), "392.65");
|
||||
EXPECT_EQ(fmt::format("{:g}", 392.65), "392.65");
|
||||
EXPECT_EQ(fmt::format("{:G}", 392.65), "392.65");
|
||||
EXPECT_EQ(fmt::format("{:g}", 4.9014e6), "4.9014e+06");
|
||||
EXPECT_EQ(fmt::format("{:f}", 392.65), "392.650000");
|
||||
EXPECT_EQ(fmt::format("{:F}", 392.65), "392.650000");
|
||||
EXPECT_EQ(fmt::format("{:L}", 42.0), "42");
|
||||
EXPECT_EQ(fmt::format("{:24a}", 4.2), " 0x1.0cccccccccccdp+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];
|
||||
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);
|
||||
EXPECT_EQ(buffer, fmt::format("{:a}", -42.0));
|
||||
EXPECT_EQ(fmt::format("{:a}", -42.0), buffer);
|
||||
safe_sprintf(buffer, "%A", -42.0);
|
||||
EXPECT_EQ(buffer, fmt::format("{:A}", -42.0));
|
||||
EXPECT_EQ("9223372036854775808.000000",
|
||||
fmt::format("{:f}", 9223372036854775807.0));
|
||||
EXPECT_EQ(fmt::format("{:A}", -42.0), buffer);
|
||||
EXPECT_EQ(fmt::format("{:f}", 9223372036854775807.0),
|
||||
"9223372036854775808.000000");
|
||||
}
|
||||
|
||||
TEST(format_test, precision_rounding) {
|
||||
|
@ -1363,6 +1460,9 @@ TEST(format_test, format_char) {
|
|||
<< format_str;
|
||||
}
|
||||
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) {
|
||||
|
@ -1393,7 +1493,11 @@ TEST(format_test, format_pointer) {
|
|||
EXPECT_EQ("0x0", fmt::format("{0}", static_cast<void*>(nullptr)));
|
||||
EXPECT_EQ("0x1234", fmt::format("{0}", reinterpret_cast<void*>(0x1234)));
|
||||
EXPECT_EQ("0x1234", fmt::format("{0:p}", reinterpret_cast<void*>(0x1234)));
|
||||
EXPECT_EQ("0x" + std::string(sizeof(void*) * CHAR_BIT / 4, 'f'),
|
||||
// On CHERI (or other fat-pointer) systems, the size of a pointer is greater
|
||||
// than the size an integer that can hold a virtual address. There is no
|
||||
// portable address-as-an-integer type (yet) in C++, so we use `size_t` as
|
||||
// the closest equivalent for now.
|
||||
EXPECT_EQ("0x" + std::string(sizeof(size_t) * CHAR_BIT / 4, 'f'),
|
||||
fmt::format("{0}", reinterpret_cast<void*>(~uintptr_t())));
|
||||
EXPECT_EQ("0x1234",
|
||||
fmt::format("{}", fmt::ptr(reinterpret_cast<int*>(0x1234))));
|
||||
|
@ -1409,14 +1513,43 @@ TEST(format_test, format_pointer) {
|
|||
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) {
|
||||
EXPECT_EQ("test", fmt::format("{0}", std::string("test")));
|
||||
EXPECT_EQ(fmt::format("{0}", std::string("test")), "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);
|
||||
}
|
||||
|
||||
TEST(format_test, format_string_view) {
|
||||
EXPECT_EQ("test", fmt::format("{}", string_view("test")));
|
||||
EXPECT_EQ("\"t\\nst\"", fmt::format("{:?}", string_view("t\nst")));
|
||||
EXPECT_EQ("", fmt::format("{}", string_view()));
|
||||
}
|
||||
|
||||
|
@ -1623,6 +1756,7 @@ TEST(format_test, group_digits_view) {
|
|||
}
|
||||
|
||||
enum test_enum { foo, bar };
|
||||
auto format_as(test_enum e) -> int { return e; }
|
||||
|
||||
TEST(format_test, join) {
|
||||
using fmt::join;
|
||||
|
@ -1652,9 +1786,15 @@ TEST(format_test, join) {
|
|||
}
|
||||
|
||||
#ifdef __cpp_lib_byte
|
||||
TEST(format_test, format_byte) {
|
||||
using arg_mapper = fmt::detail::arg_mapper<fmt::format_context>;
|
||||
EXPECT_EQ(arg_mapper().map(std::byte(42)), 42);
|
||||
EXPECT_EQ(fmt::format("{}", std::byte(42)), "42");
|
||||
}
|
||||
|
||||
TEST(format_test, join_bytes) {
|
||||
auto v = std::vector<std::byte>{std::byte(1), std::byte(2), std::byte(3)};
|
||||
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, ", ")));
|
||||
EXPECT_EQ(fmt::format("{}", fmt::join(v, ", ")), "1, 2, 3");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1694,20 +1834,21 @@ fmt::string_view to_string_view(string_like) { return "foo"; }
|
|||
|
||||
constexpr char with_null[3] = {'{', '}', '\0'};
|
||||
constexpr char no_null[2] = {'{', '}'};
|
||||
static FMT_CONSTEXPR_DECL const char static_with_null[3] = {'{', '}', '\0'};
|
||||
static FMT_CONSTEXPR_DECL const char static_no_null[2] = {'{', '}'};
|
||||
static constexpr const char static_with_null[3] = {'{', '}', '\0'};
|
||||
static constexpr const char static_no_null[2] = {'{', '}'};
|
||||
|
||||
TEST(format_test, compile_time_string) {
|
||||
EXPECT_EQ("foo", fmt::format(FMT_STRING("foo")));
|
||||
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
|
||||
EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like()));
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
using namespace fmt::literals;
|
||||
EXPECT_EQ("foobar", fmt::format(FMT_STRING("{foo}{bar}"), "bar"_a = "bar",
|
||||
"foo"_a = "foo"));
|
||||
EXPECT_EQ("", fmt::format(FMT_STRING("")));
|
||||
EXPECT_EQ("", fmt::format(FMT_STRING(""), "arg"_a = 42));
|
||||
EXPECT_EQ("42", fmt::format(FMT_STRING("{answer}"), "answer"_a = Answer()));
|
||||
#endif
|
||||
|
||||
(void)static_with_null;
|
||||
|
@ -1719,11 +1860,11 @@ TEST(format_test, compile_time_string) {
|
|||
|
||||
(void)with_null;
|
||||
(void)no_null;
|
||||
#if __cplusplus >= 201703L
|
||||
#if FMT_CPLUSPLUS >= 201703L
|
||||
EXPECT_EQ("42", fmt::format(FMT_STRING(with_null), 42));
|
||||
EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42));
|
||||
#endif
|
||||
#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L
|
||||
#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L
|
||||
EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42));
|
||||
#endif
|
||||
}
|
||||
|
@ -1739,44 +1880,16 @@ TEST(format_test, custom_format_compile_time_string) {
|
|||
}
|
||||
|
||||
#if FMT_USE_USER_DEFINED_LITERALS
|
||||
// Passing user-defined literals directly to EXPECT_EQ causes problems
|
||||
// with macro argument stringification (#) on some versions of GCC.
|
||||
// Workaround: Assing the UDL result to a variable before the macro.
|
||||
|
||||
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) {
|
||||
EXPECT_EQ("{}c{}"_format("ab", 1), fmt::format("{}c{}", "ab", 1));
|
||||
EXPECT_EQ("foo"_format(), "foo");
|
||||
EXPECT_EQ("{0:10}"_format(42), " 42");
|
||||
EXPECT_EQ("{}"_format(date(2015, 10, 21)), "2015-10-21");
|
||||
}
|
||||
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
|
||||
TEST(format_test, named_arg_udl) {
|
||||
using namespace fmt::literals;
|
||||
auto udl_a = fmt::format("{first}{second}{first}{third}", "first"_a = "abra",
|
||||
"second"_a = "cad", "third"_a = 99);
|
||||
EXPECT_EQ(
|
||||
fmt::format("{first}{second}{first}{third}", fmt::arg("first", "abra"),
|
||||
fmt::arg("second", "cad"), fmt::arg("third", 99)),
|
||||
udl_a);
|
||||
|
||||
EXPECT_EQ("42", fmt::format("{answer}", "answer"_a = Answer()));
|
||||
}
|
||||
#endif // FMT_USE_USER_DEFINED_LITERALS
|
||||
|
||||
|
@ -1790,6 +1903,7 @@ TEST(format_test, formatter_not_specialized) {
|
|||
|
||||
#if FMT_HAS_FEATURE(cxx_strong_enums)
|
||||
enum big_enum : unsigned long long { big_enum_value = 5000000000ULL };
|
||||
auto format_as(big_enum e) -> unsigned long long { return e; }
|
||||
|
||||
TEST(format_test, strong_enum) {
|
||||
EXPECT_EQ("5000000000", fmt::format("{}", big_enum_value));
|
||||
|
@ -1871,9 +1985,11 @@ TEST(format_test, to_string) {
|
|||
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");
|
||||
EXPECT_EQ(fmt::to_string(foo), "0");
|
||||
|
||||
enum foo : unsigned char { zero };
|
||||
EXPECT_EQ(fmt::to_string(zero), "0");
|
||||
#if FMT_USE_FLOAT128
|
||||
EXPECT_EQ(fmt::to_string(__float128(0.5)), "0.5");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(format_test, output_iterators) {
|
||||
|
@ -2038,7 +2154,7 @@ TEST(format_test, format_string_errors) {
|
|||
EXPECT_ERROR_NOARGS("foo", nullptr);
|
||||
EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string");
|
||||
EXPECT_ERROR("{0:s", "unknown format specifier", date);
|
||||
# if !FMT_MSC_VER || FMT_MSC_VER >= 1916
|
||||
# if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1916
|
||||
// This causes an detail compiler error in MSVC2017.
|
||||
EXPECT_ERROR("{:{<}", "invalid fill character '{'", int);
|
||||
EXPECT_ERROR("{:10000000000}", "number is too big", int);
|
||||
|
@ -2070,7 +2186,8 @@ TEST(format_test, format_string_errors) {
|
|||
# else
|
||||
fmt::print("warning: constexpr is broken in this version of MSVC\n");
|
||||
# endif
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
using namespace fmt::literals;
|
||||
EXPECT_ERROR("{foo}", "named argument is not found", decltype("bar"_a = 42));
|
||||
EXPECT_ERROR("{foo}", "named argument is not found",
|
||||
decltype(fmt::arg("foo", 42)));
|
||||
|
@ -2116,7 +2233,7 @@ TEST(format_test, char_traits_is_not_ambiguous) {
|
|||
using namespace std;
|
||||
auto c = char_traits<char>::char_type();
|
||||
(void)c;
|
||||
#if __cplusplus >= 201103L
|
||||
#if FMT_CPLUSPLUS >= 201103L
|
||||
auto s = std::string();
|
||||
auto lval = begin(s);
|
||||
(void)lval;
|
||||
|
|
|
@ -30,8 +30,8 @@ void invoke_fmt(const uint8_t* data, size_t size) {
|
|||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(format_str.get(), *value);
|
||||
#else
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_to(message, format_str.get(), *value);
|
||||
auto buf = fmt::memory_buffer();
|
||||
fmt::format_to(std::back_inserter(buf), format_str.get(), *value);
|
||||
#endif
|
||||
} catch (std::exception&) {
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ void invoke_fmt(const uint8_t* data, size_t size) {
|
|||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(format_str, item1, item2);
|
||||
#else
|
||||
fmt::memory_buffer message;
|
||||
fmt::format_to(message, format_str, item1, item2);
|
||||
auto buf = fmt::memory_buffer();
|
||||
fmt::format_to(std::back_inserter(buf), format_str, item1, item2);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -347,7 +347,7 @@ TEST(output_redirect_test, flush_error_in_ctor) {
|
|||
|
||||
TEST(output_redirect_test, dup_error_in_ctor) {
|
||||
buffered_file f = open_buffered_file();
|
||||
int fd = (f.fileno)();
|
||||
int fd = (f.descriptor)();
|
||||
file copy = file::dup(fd);
|
||||
FMT_POSIX(close(fd));
|
||||
std::unique_ptr<output_redirect> redir{nullptr};
|
||||
|
|
|
@ -23,7 +23,7 @@ output_redirect::output_redirect(FILE* f) : file_(f) {
|
|||
write_end.dup2(fd);
|
||||
}
|
||||
|
||||
output_redirect::~output_redirect() FMT_NOEXCEPT {
|
||||
output_redirect::~output_redirect() noexcept {
|
||||
try {
|
||||
restore();
|
||||
} catch (const std::exception& e) {
|
||||
|
|
|
@ -83,7 +83,7 @@ class output_redirect {
|
|||
|
||||
public:
|
||||
explicit output_redirect(FILE* file);
|
||||
~output_redirect() FMT_NOEXCEPT;
|
||||
~output_redirect() noexcept;
|
||||
|
||||
output_redirect(const output_redirect&) = delete;
|
||||
void operator=(const output_redirect&) = delete;
|
||||
|
|
|
@ -18,7 +18,7 @@ else ()
|
|||
endif ()
|
||||
|
||||
# Workaround GTest bug https://github.com/google/googletest/issues/705.
|
||||
check_cxx_compiler_flag(
|
||||
fmt_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)
|
||||
|
|
|
@ -36,7 +36,6 @@
|
|||
#else
|
||||
# define FMT_USE_FCNTL 0
|
||||
#endif
|
||||
#define FMT_NOEXCEPT noexcept
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
# define FMT_POSIX(call) _##call
|
||||
#else
|
||||
|
@ -196,13 +195,6 @@ TEST(module_test, wformat_args) {
|
|||
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) {
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> dyn_store;
|
||||
dyn_store.push_back(fmt::arg("a42", 42));
|
||||
|
|
|
@ -14,10 +14,6 @@
|
|||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifdef fileno
|
||||
# undef fileno
|
||||
#endif
|
||||
|
||||
using fmt::buffered_file;
|
||||
using testing::HasSubstr;
|
||||
using wstring_view = fmt::basic_string_view<wchar_t>;
|
||||
|
@ -205,7 +201,7 @@ TEST(buffered_file_test, move_assignment) {
|
|||
TEST(buffered_file_test, move_assignment_closes_file) {
|
||||
buffered_file bf = open_buffered_file();
|
||||
buffered_file bf2 = open_buffered_file();
|
||||
int old_fd = bf2.fileno();
|
||||
int old_fd = bf2.descriptor();
|
||||
bf2 = std::move(bf);
|
||||
EXPECT_TRUE(isclosed(old_fd));
|
||||
}
|
||||
|
@ -225,7 +221,7 @@ TEST(buffered_file_test, move_from_temporary_in_assignment) {
|
|||
|
||||
TEST(buffered_file_test, move_from_temporary_in_assignment_closes_file) {
|
||||
buffered_file f = open_buffered_file();
|
||||
int old_fd = f.fileno();
|
||||
int old_fd = f.descriptor();
|
||||
f = open_buffered_file();
|
||||
EXPECT_TRUE(isclosed(old_fd));
|
||||
}
|
||||
|
@ -234,7 +230,7 @@ TEST(buffered_file_test, close_file_in_dtor) {
|
|||
int fd = 0;
|
||||
{
|
||||
buffered_file f = open_buffered_file();
|
||||
fd = f.fileno();
|
||||
fd = f.descriptor();
|
||||
}
|
||||
EXPECT_TRUE(isclosed(fd));
|
||||
}
|
||||
|
@ -249,7 +245,7 @@ TEST(buffered_file_test, close_error_in_dtor) {
|
|||
// otherwise the system may recycle closed file descriptor when
|
||||
// redirecting the output in EXPECT_STDERR and the second close
|
||||
// will break output redirection.
|
||||
FMT_POSIX(close(f->fileno()));
|
||||
FMT_POSIX(close(f->descriptor()));
|
||||
SUPPRESS_ASSERT(f.reset(nullptr));
|
||||
},
|
||||
system_error_message(EBADF, "cannot close file") + "\n");
|
||||
|
@ -257,7 +253,7 @@ TEST(buffered_file_test, close_error_in_dtor) {
|
|||
|
||||
TEST(buffered_file_test, close) {
|
||||
buffered_file f = open_buffered_file();
|
||||
int fd = f.fileno();
|
||||
int fd = f.descriptor();
|
||||
f.close();
|
||||
EXPECT_TRUE(f.get() == nullptr);
|
||||
EXPECT_TRUE(isclosed(fd));
|
||||
|
@ -265,15 +261,15 @@ TEST(buffered_file_test, close) {
|
|||
|
||||
TEST(buffered_file_test, close_error) {
|
||||
buffered_file f = open_buffered_file();
|
||||
FMT_POSIX(close(f.fileno()));
|
||||
FMT_POSIX(close(f.descriptor()));
|
||||
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
|
||||
EXPECT_TRUE(f.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST(buffered_file_test, fileno) {
|
||||
TEST(buffered_file_test, descriptor) {
|
||||
auto f = open_buffered_file();
|
||||
EXPECT_TRUE(f.fileno() != -1);
|
||||
file copy = file::dup(f.fileno());
|
||||
EXPECT_TRUE(f.descriptor() != -1);
|
||||
file copy = file::dup(f.descriptor());
|
||||
EXPECT_READ(copy, file_content);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
using fmt::runtime;
|
||||
|
@ -30,12 +32,12 @@ template <> struct formatter<test> : formatter<int> {
|
|||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const date& d) {
|
||||
auto operator<<(std::ostream& os, const date& d) -> std::ostream& {
|
||||
os << d.year() << '-' << d.month() << '-' << d.day();
|
||||
return os;
|
||||
}
|
||||
|
||||
std::wostream& operator<<(std::wostream& os, const date& d) {
|
||||
auto operator<<(std::wostream& os, const date& d) -> std::wostream& {
|
||||
os << d.year() << L'-' << d.month() << L'-' << d.day();
|
||||
return os;
|
||||
}
|
||||
|
@ -47,11 +49,24 @@ template <typename T> type_with_comma_op operator<<(T&, const date&);
|
|||
|
||||
enum streamable_enum {};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, streamable_enum) {
|
||||
auto operator<<(std::ostream& os, streamable_enum) -> std::ostream& {
|
||||
return os << "streamable_enum";
|
||||
}
|
||||
|
||||
enum unstreamable_enum {};
|
||||
auto format_as(unstreamable_enum e) -> int { return e; }
|
||||
|
||||
struct empty_test {};
|
||||
auto operator<<(std::ostream& os, empty_test) -> std::ostream& {
|
||||
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) {
|
||||
EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_enum()));
|
||||
|
@ -86,9 +101,6 @@ TEST(ostream_test, format_specs) {
|
|||
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) {
|
||||
EXPECT_EQ("", fmt::format("{}", empty_test()));
|
||||
}
|
||||
|
@ -121,7 +133,7 @@ TEST(ostream_test, write_to_ostream_max_size) {
|
|||
|
||||
struct mock_streambuf : std::streambuf {
|
||||
MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n));
|
||||
std::streamsize xsputn(const char* s, std::streamsize n) override {
|
||||
auto xsputn(const char* s, std::streamsize n) -> std::streamsize override {
|
||||
const void* v = s;
|
||||
return xsputn(v, n);
|
||||
}
|
||||
|
@ -158,15 +170,16 @@ TEST(ostream_test, join_fallback_formatter) {
|
|||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
TEST(ostream_test, constexpr_string) {
|
||||
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
|
||||
EXPECT_EQ("a string", format(FMT_STRING("{0}"), test_string("a string")));
|
||||
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), std::string("42")));
|
||||
EXPECT_EQ("a string",
|
||||
fmt::format(FMT_STRING("{0}"), test_string("a string")));
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace fmt_test {
|
||||
struct abc {};
|
||||
|
||||
template <typename Output> Output& operator<<(Output& out, abc) {
|
||||
template <typename Output> auto operator<<(Output& out, abc) -> Output& {
|
||||
return out << "abc";
|
||||
}
|
||||
} // namespace fmt_test
|
||||
|
@ -174,7 +187,7 @@ template <typename Output> Output& operator<<(Output& out, abc) {
|
|||
template <typename T> struct test_template {};
|
||||
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream& os, test_template<T>) {
|
||||
auto operator<<(std::ostream& os, test_template<T>) -> std::ostream& {
|
||||
return os << 1;
|
||||
}
|
||||
|
||||
|
@ -184,6 +197,8 @@ template <typename T> struct formatter<test_template<T>> : formatter<int> {
|
|||
return formatter<int>::format(2, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct formatter<fmt_test::abc> : ostream_formatter {};
|
||||
} // namespace fmt
|
||||
|
||||
TEST(ostream_test, template) {
|
||||
|
@ -214,41 +229,6 @@ TEST(ostream_test, disable_builtin_ostream_operators) {
|
|||
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 {
|
||||
operator bool() const { return true; }
|
||||
};
|
||||
|
@ -262,6 +242,21 @@ TEST(ostream_test, format_convertible_to_bool) {
|
|||
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 {};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, copyfmt_test) {
|
||||
|
@ -270,6 +265,10 @@ std::ostream& operator<<(std::ostream& os, copyfmt_test) {
|
|||
return os << "foo";
|
||||
}
|
||||
|
||||
namespace fmt {
|
||||
template <> struct formatter<copyfmt_test> : ostream_formatter {};
|
||||
} // namespace fmt
|
||||
|
||||
TEST(ostream_test, copyfmt) {
|
||||
EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
|
||||
}
|
||||
|
@ -286,11 +285,15 @@ TEST(ostream_test, range) {
|
|||
struct abstract {
|
||||
virtual ~abstract() = default;
|
||||
virtual void f() = 0;
|
||||
friend std::ostream& operator<<(std::ostream& os, const abstract&) {
|
||||
friend auto operator<<(std::ostream& os, const abstract&) -> std::ostream& {
|
||||
return os;
|
||||
}
|
||||
};
|
||||
|
||||
namespace fmt {
|
||||
template <> struct formatter<abstract> : ostream_formatter {};
|
||||
} // namespace fmt
|
||||
|
||||
void format_abstract_compiles(const abstract& a) {
|
||||
fmt::format(FMT_COMPILE("{}"), a);
|
||||
}
|
||||
|
@ -299,3 +302,21 @@ TEST(ostream_test, is_formattable) {
|
|||
EXPECT_TRUE(fmt::is_formattable<std::string>());
|
||||
EXPECT_TRUE(fmt::is_formattable<fmt::detail::std_string_view<char>>());
|
||||
}
|
||||
|
||||
struct streamable_and_unformattable {};
|
||||
|
||||
auto operator<<(std::ostream& os, streamable_and_unformattable)
|
||||
-> std::ostream& {
|
||||
return os << "foo";
|
||||
}
|
||||
|
||||
TEST(ostream_test, streamed) {
|
||||
EXPECT_FALSE(fmt::is_formattable<streamable_and_unformattable>());
|
||||
EXPECT_EQ(fmt::format("{}", fmt::streamed(streamable_and_unformattable())),
|
||||
"foo");
|
||||
}
|
||||
|
||||
TEST(ostream_test, closed_ofstream) {
|
||||
std::ofstream ofs;
|
||||
fmt::print(ofs, "discard");
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// For the license information refer to format.h.
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
|
@ -438,7 +438,7 @@ TEST(buffered_file_test, fileno_no_retry) {
|
|||
file::pipe(read_end, write_end);
|
||||
buffered_file f = read_end.fdopen("r");
|
||||
fileno_count = 1;
|
||||
EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor");
|
||||
EXPECT_SYSTEM_ERROR((f.descriptor)(), EINTR, "cannot get file descriptor");
|
||||
EXPECT_EQ(2, fileno_count);
|
||||
fileno_count = 0;
|
||||
}
|
||||
|
@ -457,84 +457,3 @@ TEST(scoped_mock, scope) {
|
|||
}
|
||||
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));
|
||||
} * 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);
|
||||
}
|
||||
# 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();
|
||||
}
|
||||
|
||||
# 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());
|
||||
}
|
||||
|
||||
#endif // FMT_LOCALE
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include <climits>
|
||||
#include <cstring>
|
||||
|
||||
#include "fmt/ostream.h"
|
||||
#include "fmt/xchar.h"
|
||||
#include "gtest-extra.h"
|
||||
#include "util.h"
|
||||
|
@ -505,6 +504,7 @@ TEST(printf_test, pointer) {
|
|||
}
|
||||
|
||||
enum test_enum { answer = 42 };
|
||||
auto format_as(test_enum e) -> int { return e; }
|
||||
|
||||
TEST(printf_test, enum) {
|
||||
EXPECT_PRINTF("42", "%d", answer);
|
||||
|
@ -533,10 +533,6 @@ TEST(printf_test, wide_string) {
|
|||
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) {
|
||||
fmt::format_arg_store<fmt::printf_context, int> as{42};
|
||||
fmt::basic_format_args<fmt::printf_context> args(as);
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
# define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
|
||||
#endif
|
||||
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1910
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1910
|
||||
# define FMT_RANGES_TEST_ENABLE_JOIN
|
||||
# define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
|
||||
#endif
|
||||
|
@ -46,11 +46,13 @@ TEST(ranges_test, format_array_of_literals) {
|
|||
TEST(ranges_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]");
|
||||
EXPECT_EQ(fmt::format("{::#x}", v), "[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]");
|
||||
}
|
||||
|
||||
TEST(ranges_test, format_vector2) {
|
||||
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("{:::#x}", v), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]");
|
||||
}
|
||||
|
||||
TEST(ranges_test, format_map) {
|
||||
|
@ -63,16 +65,42 @@ TEST(ranges_test, format_set) {
|
|||
"{\"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) {
|
||||
auto p = std::pair<int, float>(42, 1.5f);
|
||||
EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)");
|
||||
}
|
||||
|
||||
struct unformattable {};
|
||||
|
||||
TEST(ranges_test, format_tuple) {
|
||||
auto t =
|
||||
std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i');
|
||||
EXPECT_EQ(fmt::format("{}", t), "(42, 1.5, \"this is tuple\", 'i')");
|
||||
EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()");
|
||||
|
||||
EXPECT_TRUE((fmt::is_formattable<std::tuple<>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable, int>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::tuple<int, unformattable>>::value));
|
||||
EXPECT_FALSE(
|
||||
(fmt::is_formattable<std::tuple<unformattable, unformattable>>::value));
|
||||
EXPECT_TRUE((fmt::is_formattable<std::tuple<int, float>>::value));
|
||||
}
|
||||
|
||||
#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
|
||||
|
@ -131,8 +159,8 @@ TEST(ranges_test, path_like) {
|
|||
struct string_like {
|
||||
const char* begin();
|
||||
const char* end();
|
||||
explicit operator fmt::string_view() const { return "foo"; }
|
||||
explicit operator std::string_view() const { return "foo"; }
|
||||
operator fmt::string_view() const { return "foo"; }
|
||||
operator std::string_view() const { return "foo"; }
|
||||
};
|
||||
|
||||
TEST(ranges_test, format_string_like) {
|
||||
|
@ -196,14 +224,14 @@ TEST(ranges_test, range) {
|
|||
}
|
||||
|
||||
enum test_enum { foo };
|
||||
auto format_as(test_enum e) -> int { return e; }
|
||||
|
||||
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 {};
|
||||
#if !FMT_MSC_VERSION
|
||||
|
||||
TEST(ranges_test, unformattable_range) {
|
||||
EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>,
|
||||
|
@ -296,6 +324,7 @@ static_assert(std::input_iterator<cpp20_only_range::iterator>);
|
|||
TEST(ranges_test, join_sentinel) {
|
||||
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("{}", fmt::join(hello, "_")), "h_e_l_l_o");
|
||||
}
|
||||
|
||||
|
@ -348,6 +377,7 @@ TEST(ranges_test, escape_string) {
|
|||
EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]");
|
||||
EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}),
|
||||
"[\"\\xf4\\x8f\\xbf\\xc0\"]");
|
||||
EXPECT_EQ(fmt::format("{}", vec{"понедельник"}), "[\"понедельник\"]");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,3 +391,18 @@ TEST(ranges_test, escape_convertible_to_string_view) {
|
|||
"[\"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]]");
|
||||
}
|
||||
|
|
84
test/std-test.cc
Normal file
84
test/std-test.cc
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Formatting library for C++ - tests of formatters for standard library types
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#include "fmt/std.h"
|
||||
#include "fmt/ranges.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
TEST(std_test, path) {
|
||||
// Test ambiguity problem described in #2954. We need to exclude compilers
|
||||
// where the ambiguity problem cannot be solved for now.
|
||||
#if defined(__cpp_lib_filesystem) && \
|
||||
(!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920)
|
||||
EXPECT_EQ(fmt::format("{:8}", std::filesystem::path("foo")), "\"foo\" ");
|
||||
EXPECT_EQ(fmt::format("{}", std::filesystem::path("foo\"bar.txt")),
|
||||
"\"foo\\\"bar.txt\"");
|
||||
|
||||
# ifdef _WIN32
|
||||
// File.txt in Russian.
|
||||
const wchar_t unicode_path[] = {0x424, 0x430, 0x439, 0x43b, 0x2e,
|
||||
0x74, 0x78, 0x74, 0};
|
||||
const char unicode_u8path[] = {'"', char(0xd0), char(0xa4), char(0xd0),
|
||||
char(0xb0), char(0xd0), char(0xb9), char(0xd0),
|
||||
char(0xbb), '.', 't', 'x',
|
||||
't', '"', '\0'};
|
||||
EXPECT_EQ(fmt::format("{}", std::filesystem::path(unicode_path)),
|
||||
unicode_u8path);
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(ranges_std_test, format_vector_path) {
|
||||
// Test ambiguity problem described in #2954. We need to exclude compilers
|
||||
// where the ambiguity problem cannot be solved for now.
|
||||
#if defined(__cpp_lib_filesystem) && \
|
||||
(!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920)
|
||||
auto p = std::filesystem::path("foo/bar.txt");
|
||||
auto c = std::vector<std::string>{"abc", "def"};
|
||||
EXPECT_EQ(fmt::format("path={}, range={}", p, c),
|
||||
"path=\"foo/bar.txt\", range=[\"abc\", \"def\"]");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(std_test, thread_id) {
|
||||
EXPECT_FALSE(fmt::format("{}", std::this_thread::get_id()).empty());
|
||||
}
|
||||
|
||||
TEST(std_test, variant) {
|
||||
#ifdef __cpp_lib_variant
|
||||
EXPECT_EQ(fmt::format("{}", std::monostate{}), "monostate");
|
||||
using V0 = std::variant<int, float, std::string, char>;
|
||||
V0 v0(42);
|
||||
V0 v1(1.5f);
|
||||
V0 v2("hello");
|
||||
V0 v3('i');
|
||||
EXPECT_EQ(fmt::format("{}", v0), "variant(42)");
|
||||
EXPECT_EQ(fmt::format("{}", v1), "variant(1.5)");
|
||||
EXPECT_EQ(fmt::format("{}", v2), "variant(\"hello\")");
|
||||
EXPECT_EQ(fmt::format("{}", v3), "variant('i')");
|
||||
|
||||
struct unformattable {};
|
||||
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<unformattable, int>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::variant<int, unformattable>>::value));
|
||||
EXPECT_FALSE(
|
||||
(fmt::is_formattable<std::variant<unformattable, unformattable>>::value));
|
||||
EXPECT_TRUE((fmt::is_formattable<std::variant<int, float>>::value));
|
||||
|
||||
using V1 = std::variant<std::monostate, std::string, std::string>;
|
||||
V1 v4{};
|
||||
V1 v5{std::in_place_index<1>, "yes, this is variant"};
|
||||
|
||||
EXPECT_EQ(fmt::format("{}", v4), "variant(monostate)");
|
||||
EXPECT_EQ(fmt::format("{}", v5), "variant(\"yes, this is variant\")");
|
||||
#endif
|
||||
}
|
|
@ -15,9 +15,6 @@
|
|||
|
||||
#ifdef _MSC_VER
|
||||
# include <crtdbg.h>
|
||||
#else
|
||||
# define _CrtSetReportFile(a, b)
|
||||
# define _CrtSetReportMode(a, b)
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
@ -28,11 +25,13 @@ int main(int argc, char** argv) {
|
|||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
|
||||
SEM_NOOPENFILEERRORBOX);
|
||||
#endif
|
||||
#ifdef _MSC_VER
|
||||
// Disable message boxes on assertion failures.
|
||||
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
|
||||
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
|
||||
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
|
||||
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
|
||||
#endif
|
||||
try {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||
|
|
|
@ -66,14 +66,16 @@ TYPED_TEST(is_string_test, is_string) {
|
|||
}
|
||||
|
||||
// std::is_constructible is broken in MSVC until version 2015.
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1900
|
||||
struct explicitly_convertible_to_wstring_view {
|
||||
explicit operator fmt::wstring_view() const { return L"foo"; }
|
||||
};
|
||||
|
||||
TEST(xchar_test, format_explicitly_convertible_to_wstring_view) {
|
||||
EXPECT_EQ(L"foo",
|
||||
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
|
||||
// Types explicitly convertible to wstring_view are not formattable by
|
||||
// default because it may introduce ODR violations.
|
||||
static_assert(
|
||||
!fmt::is_formattable<explicitly_convertible_to_wstring_view>::value, "");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -96,12 +98,12 @@ TEST(xchar_test, is_formattable) {
|
|||
}
|
||||
|
||||
TEST(xchar_test, compile_time_string) {
|
||||
#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L
|
||||
#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L
|
||||
EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if __cplusplus > 201103L
|
||||
#if FMT_CPLUSPLUS > 201103L
|
||||
struct custom_char {
|
||||
int value;
|
||||
custom_char() = default;
|
||||
|
@ -183,11 +185,6 @@ TEST(format_test, wide_format_to_n) {
|
|||
}
|
||||
|
||||
#if FMT_USE_USER_DEFINED_LITERALS
|
||||
TEST(xchar_test, format_udl) {
|
||||
using namespace fmt::literals;
|
||||
EXPECT_EQ(L"{}c{}"_format(L"ab", 1), fmt::format(L"{}c{}", L"ab", 1));
|
||||
}
|
||||
|
||||
TEST(xchar_test, named_arg_udl) {
|
||||
using namespace fmt::literals;
|
||||
auto udl_a =
|
||||
|
@ -218,7 +215,14 @@ std::wostream& operator<<(std::wostream& os, 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 {};
|
||||
auto format_as(unstreamable_enum e) -> int { return e; }
|
||||
|
||||
TEST(xchar_test, enum) {
|
||||
EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_enum()));
|
||||
|
@ -232,28 +236,6 @@ TEST(xchar_test, sign_not_truncated) {
|
|||
EXPECT_THROW(fmt::format(format_str, 42), fmt::format_error);
|
||||
}
|
||||
|
||||
namespace fake_qt {
|
||||
class QString {
|
||||
public:
|
||||
QString(const wchar_t* s) : s_(s) {}
|
||||
const wchar_t* utf16() const FMT_NOEXCEPT { return s_.data(); }
|
||||
int size() const FMT_NOEXCEPT { return static_cast<int>(s_.size()); }
|
||||
|
||||
private:
|
||||
std::wstring s_;
|
||||
};
|
||||
|
||||
fmt::basic_string_view<wchar_t> to_string_view(const QString& s) FMT_NOEXCEPT {
|
||||
return {s.utf16(), static_cast<size_t>(s.size())};
|
||||
}
|
||||
} // namespace fake_qt
|
||||
|
||||
TEST(format_test, format_foreign_strings) {
|
||||
using fake_qt::QString;
|
||||
EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
|
||||
EXPECT_EQ(fmt::format(QString(L"{}"), QString(L"42")), L"42");
|
||||
}
|
||||
|
||||
TEST(xchar_test, chrono) {
|
||||
auto tm = std::tm();
|
||||
tm.tm_year = 116;
|
||||
|
@ -322,9 +304,22 @@ TEST(xchar_test, color) {
|
|||
}
|
||||
|
||||
TEST(xchar_test, ostream) {
|
||||
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409
|
||||
std::wostringstream wos;
|
||||
fmt::print(wos, L"Don't {}!", L"panic");
|
||||
EXPECT_EQ(L"Don't panic!", wos.str());
|
||||
EXPECT_EQ(wos.str(), L"Don't panic!");
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(xchar_test, format_map) {
|
||||
auto m = std::map<std::wstring, int>{{L"one", 1}, {L"t\"wo", 2}};
|
||||
EXPECT_EQ(fmt::format(L"{}", m), L"{\"one\": 1, \"t\\\"wo\": 2}");
|
||||
}
|
||||
|
||||
TEST(xchar_test, escape_string) {
|
||||
using vec = std::vector<std::wstring>;
|
||||
EXPECT_EQ(fmt::format(L"{}", vec{L"\n\r\t\"\\"}), L"[\"\\n\\r\\t\\\"\\\\\"]");
|
||||
EXPECT_EQ(fmt::format(L"{}", vec{L"понедельник"}), L"[\"понедельник\"]");
|
||||
}
|
||||
|
||||
TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); }
|
||||
|
@ -360,10 +355,11 @@ template <typename Char> struct small_grouping : std::numpunct<Char> {
|
|||
|
||||
TEST(locale_test, localized_double) {
|
||||
auto loc = std::locale(std::locale(), new numpunct<char>());
|
||||
EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23));
|
||||
EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23));
|
||||
EXPECT_EQ("1~234?5", fmt::format(loc, "{:L}", 1234.5));
|
||||
EXPECT_EQ("12~000", fmt::format(loc, "{:L}", 12000.0));
|
||||
EXPECT_EQ(fmt::format(loc, "{:L}", 1.23), "1?23");
|
||||
EXPECT_EQ(fmt::format(loc, "{:Lf}", 1.23), "1?230000");
|
||||
EXPECT_EQ(fmt::format(loc, "{:L}", 1234.5), "1~234?5");
|
||||
EXPECT_EQ(fmt::format(loc, "{:L}", 12000.0), "12~000");
|
||||
EXPECT_EQ(fmt::format(loc, "{:8L}", 1230.0), " 1~230");
|
||||
}
|
||||
|
||||
TEST(locale_test, format) {
|
||||
|
|
Loading…
Reference in a new issue