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:
Merry 2022-07-26 10:51:24 +01:00
parent 6633089a44
commit a7f9129f18
58 changed files with 4777 additions and 3816 deletions

6
.github/issue_template.md vendored Normal file
View 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.
-->

View file

@ -2,6 +2,9 @@ name: doc
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
build: build:
# Use Ubuntu 20.04 because doxygen 1.8.13 from Ubuntu 18.04 is broken. # Use Ubuntu 20.04 because doxygen 1.8.13 from Ubuntu 18.04 is broken.

View file

@ -2,6 +2,9 @@ name: linux
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
build: build:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@ -20,6 +23,11 @@ jobs:
std: 14 std: 14
install: sudo apt install g++-8 install: sudo apt install g++-8
os: ubuntu-18.04 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 - cxx: g++-10
build_type: Debug build_type: Debug
std: 17 std: 17
@ -29,6 +37,12 @@ jobs:
std: 20 std: 20
os: ubuntu-20.04 os: ubuntu-20.04
install: sudo apt install g++-11 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 - cxx: clang++-9
build_type: Debug build_type: Debug
fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON fuzz: -DFMT_FUZZ=ON -DFMT_FUZZ_LINKMAIN=ON
@ -52,6 +66,7 @@ jobs:
- name: Create Build Environment - name: Create Build Environment
run: | run: |
${{matrix.install}} ${{matrix.install}}
sudo apt update
sudo apt install locales-all sudo apt install locales-all
cmake -E make_directory ${{runner.workspace}}/build cmake -E make_directory ${{runner.workspace}}/build

View file

@ -2,6 +2,9 @@ name: macos
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
build: build:
runs-on: macos-10.15 runs-on: macos-10.15

View file

@ -2,31 +2,34 @@ name: windows
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
build: build:
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
strategy: strategy:
matrix: matrix:
# windows-2016 and windows-2019 have MSVC 2017 and 2019 installed # windows-2019 has MSVC 2019 installed;
# respectively: https://github.com/actions/virtual-environments. # windows-2022 has MSVC 2022 installed:
os: [windows-2016, windows-2019] # https://github.com/actions/virtual-environments.
os: [windows-2019]
platform: [Win32, x64] platform: [Win32, x64]
build_type: [Debug, Release] build_type: [Debug, Release]
standard: [11, 17, 20] standard: [11, 17, 20]
include: include:
- os: windows-2016 - os: windows-2019
platform: Win32 platform: Win32
build_type: Debug build_type: Debug
shared: -DBUILD_SHARED_LIBS=ON shared: -DBUILD_SHARED_LIBS=ON
exclude: - os: windows-2022
- os: windows-2016 platform: x64
platform: Win32 build_type: Debug
- os: windows-2016
standard: 17
- os: windows-2016
standard: 20 standard: 20
exclude:
- os: windows-2019 - os: windows-2019
standard: 11 standard: 11
platform: Win32
- os: windows-2019 - os: windows-2019
standard: 20 standard: 20
platform: Win32 platform: Win32

View file

@ -125,7 +125,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake") "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
include(cxx14) include(cxx14)
include(CheckCXXCompilerFlag)
include(JoinPaths) include(JoinPaths)
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index) 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}\" %*") ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
endif () endif ()
set(strtod_l_headers stdlib.h)
if (APPLE)
set(strtod_l_headers ${strtod_l_headers} xlocale.h)
endif ()
include(CheckSymbolExists)
if (WIN32)
check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
else ()
check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
endif ()
function(add_headers VAR) function(add_headers VAR)
set(headers ${${VAR}}) set(headers ${${VAR}})
foreach (header ${ARGN}) foreach (header ${ARGN})
@ -231,7 +218,7 @@ endfunction()
# Define the fmt library, its includes and the needed defines. # Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h 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) xchar.h)
if (FMT_MODULE) if (FMT_MODULE)
set(FMT_SOURCES src/fmt.cc) 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_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt) add_library(fmt::fmt ALIAS fmt)
if (HAVE_STRTOD_L)
target_compile_definitions(fmt PUBLIC FMT_LOCALE)
endif ()
if (MINGW)
check_cxx_compiler_flag("-Wa,-mbig-obj" FMT_HAS_MBIG_OBJ)
if (${FMT_HAS_MBIG_OBJ})
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
endif()
endif ()
if (FMT_WERROR) if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG}) target_compile_options(fmt PRIVATE ${WERROR_FLAG})
endif () endif ()
@ -275,6 +251,7 @@ set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
set_target_properties(fmt PROPERTIES set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR} VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
PUBLIC_HEADER "${FMT_HEADERS}"
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}") DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target # Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
@ -352,6 +329,8 @@ if (FMT_INSTALL)
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
LIBRARY DESTINATION ${FMT_LIB_DIR} LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR} ARCHIVE DESTINATION ${FMT_LIB_DIR}
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
FRAMEWORK DESTINATION "."
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
# Use a namespace because CMake provides better diagnostics for namespaced # Use a namespace because CMake provides better diagnostics for namespaced
@ -368,7 +347,6 @@ if (FMT_INSTALL)
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}> install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL) DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif () endif ()

View file

@ -1,3 +1,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 8.1.1 - 2022-01-06
------------------ ------------------
@ -6,7 +392,7 @@
`#2696 <https://github.com/fmtlib/fmt/pull/2696>`_). `#2696 <https://github.com/fmtlib/fmt/pull/2696>`_).
Thanks `@saraedum (Julian Rüth) <https://github.com/saraedum>`_. 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>`_, (`#2698 <https://github.com/fmtlib/fmt/issues/2698>`_,
`#2699 <https://github.com/fmtlib/fmt/pull/2699>`_). `#2699 <https://github.com/fmtlib/fmt/pull/2699>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and
@ -148,6 +534,10 @@
[" ["
aan"] 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 * Switched to JSON-like representation of maps and sets for consistency with
Python's ``str.format``. Python's ``str.format``.
For example (`godbolt <https://godbolt.org/z/seKjoY9W5>`__): For example (`godbolt <https://godbolt.org/z/seKjoY9W5>`__):
@ -367,7 +757,8 @@
`@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_, `@alexezeder (Alexey Ochapov) <https://github.com/alexezeder>`_,
`@andrewcorrigan (Andrew Corrigan) <https://github.com/andrewcorrigan>`_, `@andrewcorrigan (Andrew Corrigan) <https://github.com/andrewcorrigan>`_,
`@lucpelletier <https://github.com/lucpelletier>`_, `@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 8.0.1 - 2021-07-02
------------------ ------------------

View file

@ -1,5 +1,7 @@
{fmt} .. image:: https://user-images.githubusercontent.com/
===== 576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png
:width: 25%
:alt: {fmt}
.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg .. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux
@ -26,12 +28,13 @@
**{fmt}** is an open-source formatting library providing a fast and safe **{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams. alternative to C stdio and C++ iostreams.
If you like this project, please consider donating to the BYSOL If you like this project, please consider donating to one of the funds that
Foundation that helps victims of political repressions in Belarus: help victims of the war in Ukraine: https://www.stopputin.net/.
https://bysol.org/en/bs/general/.
`Documentation <https://fmt.dev>`__ `Documentation <https://fmt.dev>`__
`Cheat Sheets <https://hackingcpp.com/cpp/libs/fmt.html>`__
Q&A: ask questions on `StackOverflow with the tag fmt Q&A: ask questions on `StackOverflow with the tag fmt
<https://stackoverflow.com/questions/tagged/fmt>`_. <https://stackoverflow.com/questions/tagged/fmt>`_.
@ -123,7 +126,7 @@ Output::
Default format: 42s 100ms Default format: 42s 100ms
strftime-like format: 03:15:30 strftime-like format: 03:15:30
**Print a container** (`run <https://godbolt.org/z/MjsY7c>`_) **Print a container** (`run <https://godbolt.org/z/MxM1YqjE7>`_)
.. code:: c++ .. code:: c++
@ -341,9 +344,12 @@ Projects using this library
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library * `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
* `GemRB <https://gemrb.org/>`_: a portable open-source implementation of
Biowares Infinity Engine
* `Grand Mountain Adventure * `Grand Mountain Adventure
<https://store.steampowered.com/app/1247360/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>`_: * `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
Player vs Player Gaming Network with tweaks Player vs Player Gaming Network with tweaks

View file

@ -12,6 +12,7 @@ The {fmt} library API consists of the following parts:
formatting functions and locale support formatting functions and locale support
* :ref:`fmt/ranges.h <ranges-api>`: formatting of ranges and tuples * :ref:`fmt/ranges.h <ranges-api>`: formatting of ranges and tuples
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting * :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/compile.h <compile-api>`: format string compilation
* :ref:`fmt/color.h <color-api>`: terminal color and text style * :ref:`fmt/color.h <color-api>`: terminal color and text style
* :ref:`fmt/os.h <os-api>`: system APIs * :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:: print(std::FILE *f, format_string<T...> fmt, T&&... args)
.. doxygenfunction:: vprint(std::FILE *f, string_view fmt, format_args args) .. doxygenfunction:: vprint(std::FILE *f, string_view fmt, format_args args)
Compile-time Format String Checks Compile-Time Format String Checks
--------------------------------- ---------------------------------
Compile-time checks are enabled when using ``FMT_STRING``. They support built-in Compile-time checks are enabled when using ``FMT_STRING``. They support built-in
@ -113,8 +114,7 @@ binary footprint, for example (https://godbolt.org/z/oba4Mc):
template <typename S, typename... Args> template <typename S, typename... Args>
void log(const char* file, int line, const S& format, Args&&... args) { void log(const char* file, int line, const S& format, Args&&... args) {
vlog(file, line, format, vlog(file, line, format, fmt::make_format_args(args...));
fmt::make_args_checked<Args...>(format, args...));
} }
#define MY_LOG(format, ...) \ #define MY_LOG(format, ...) \
@ -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 Note that ``vlog`` is not parameterized on argument types which improves compile
times and reduces binary code size compared to a fully parameterized version. times and reduces binary code size compared to a fully parameterized version.
.. doxygenfunction:: fmt::make_args_checked(const S&, const remove_reference_t<Args>&...)
.. doxygenfunction:: fmt::make_format_args(const Args&...) .. doxygenfunction:: fmt::make_format_args(const Args&...)
.. doxygenclass:: fmt::format_arg_store .. doxygenclass:: fmt::format_arg_store
@ -143,6 +141,9 @@ times and reduces binary code size compared to a fully parameterized version.
.. doxygenclass:: fmt::basic_format_arg .. doxygenclass:: fmt::basic_format_arg
:members: :members:
.. doxygenclass:: fmt::basic_format_parse_context
:members:
.. doxygenclass:: fmt::basic_format_context .. doxygenclass:: fmt::basic_format_context
:members: :members:
@ -179,9 +180,15 @@ functions and locale support.
.. _udt: .. _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 To make a user-defined type formattable, specialize the ``formatter<T>`` struct
template and implement ``parse`` and ``format`` methods:: template and implement ``parse`` and ``format`` methods::
@ -207,6 +214,10 @@ template and implement ``parse`` and ``format`` methods::
// parse specifiers until '}' or the end of the range. In this example // parse specifiers until '}' or the end of the range. In this example
// the formatter should parse the 'f' specifier and return an iterator // the formatter should parse the 'f' specifier and return an iterator
// pointing to '}'. // 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: // Parse the presentation format and store it in the formatter:
auto it = ctx.begin(), end = ctx.end(); auto it = ctx.begin(), end = ctx.end();
@ -222,11 +233,11 @@ template and implement ``parse`` and ``format`` methods::
// Formats the point p using the parsed format specification (presentation) // Formats the point p using the parsed format specification (presentation)
// stored in this formatter. // stored in this formatter.
template <typename FormatContext> template <typename FormatContext>
auto format(const point& p, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const point& p, FormatContext& ctx) const -> decltype(ctx.out()) {
// ctx.out() is an output iterator to write to. // ctx.out() is an output iterator to write to.
return presentation == 'f' return presentation == 'f'
? format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y) ? fmt::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(), "({:.1e}, {:.1e})", p.x, p.y);
} }
}; };
@ -244,7 +255,7 @@ example::
template <> struct fmt::formatter<color>: formatter<string_view> { template <> struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>. // parse is inherited from formatter<string_view>.
template <typename FormatContext> template <typename FormatContext>
auto format(color c, FormatContext& ctx) { auto format(color c, FormatContext& ctx) const {
string_view name = "unknown"; string_view name = "unknown";
switch (c) { switch (c) {
case color::red: name = "red"; break; case color::red: name = "red"; break;
@ -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>> : struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> :
fmt::formatter<std::string> { fmt::formatter<std::string> {
template <typename FormatCtx> template <typename FormatCtx>
auto format(const A& a, FormatCtx& ctx) { auto format(const A& a, FormatCtx& ctx) const {
return fmt::formatter<std::string>::format(a.name(), ctx); return fmt::formatter<std::string>::format(a.name(), ctx);
} }
}; };
@ -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 to a formattable type, the specialization takes precedence over the
conversion. conversion.
.. doxygenclass:: fmt::basic_format_parse_context For enums {fmt} also provides the ``format_as`` extension API. To format an enum
:members: 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``. 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()
.. doxygenfunction:: operator""_a(const char *s, size_t) -> detail::udl_arg<char>
Utilities Utilities
--------- ---------
@ -316,9 +342,9 @@ Utilities
.. doxygenfunction:: fmt::ptr(const std::unique_ptr<T> &p) -> const void* .. doxygenfunction:: fmt::ptr(const std::unique_ptr<T> &p) -> const void*
.. doxygenfunction:: fmt::ptr(const std::shared_ptr<T> &p) -> const void* .. doxygenfunction:: fmt::ptr(const std::shared_ptr<T> &p) -> const void*
.. doxygenfunction:: fmt::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>> .. 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-api:
Ranges and Tuple Formatting Range and Tuple Formatting
=========================== ==========================
The library also supports convenient formatting of ranges and tuples:: 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) .. 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: .. _compile-api:
Format string compilation Format String Compilation
========================= =========================
``fmt/compile.h`` provides format string compilation support when using ``fmt/compile.h`` provides format string compilation enabled via the
``FMT_COMPILE``. Format strings are parsed, checked and converted into efficient ``FMT_COMPILE`` macro or the ``_cf`` user-defined literal. Format strings
formatting code at compile-time. This supports arguments of built-in and string marked with ``FMT_COMPILE`` or ``_cf`` are parsed, checked and converted into
types as well as user-defined types with ``constexpr`` ``parse`` functions in efficient formatting code at compile-time. This supports arguments of built-in
their ``formatter`` specializations. Format string compilation can generate more and string types as well as user-defined types with ``constexpr`` ``parse``
binary code compared to the default API and is only recommended in places where functions in their ``formatter`` specializations. Format string compilation can
formatting is a performance bottleneck. generate more binary code compared to the default API and is only recommended in
places where formatting is a performance bottleneck.
.. doxygendefine:: FMT_COMPILE .. doxygendefine:: FMT_COMPILE
.. doxygenfunction:: operator""_cf()
.. _color-api: .. _color-api:
Terminal color and text style Terminal Color and Text Style
============================= =============================
``fmt/color.h`` provides support for terminal color and text style output. ``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:: bg(detail::color_type)
.. doxygenfunction:: styled(const T& value, text_style ts)
.. _os-api: .. _os-api:
System APIs System APIs
@ -486,27 +546,28 @@ System APIs
======================== ========================
``fmt/ostream.h`` provides ``std::ostream`` support including formatting of ``fmt/ostream.h`` provides ``std::ostream`` support including formatting of
user-defined types that have an overloaded insertion operator (``operator<<``):: user-defined types that have an overloaded insertion operator (``operator<<``).
In order to make a type formattable via ``std::ostream`` you should provide a
``formatter`` specialization inherited from ``ostream_formatter``::
#include <fmt/ostream.h> #include <fmt/ostream.h>
class date { struct date {
int year_, month_, day_; int year, month, day;
public:
date(int year, int month, int day): year_(year), month_(month), day_(day) {}
friend std::ostream& operator<<(std::ostream& os, const date& d) { 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" // s == "The date is 2012-12-9"
{fmt} only supports insertion operators that are defined in the same namespaces .. doxygenfunction:: streamed(const T &)
as the types they format and can be found with the argument-dependent lookup.
.. doxygenfunction:: print(std::basic_ostream<Char> &os, const S &format_str, Args&&... args) .. doxygenfunction:: print(std::ostream &os, format_string<T...> fmt, T&&... args)
.. _printf-api: .. _printf-api:

View file

@ -4,7 +4,7 @@
import errno, os, re, sys import errno, os, re, sys
from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2', '7.1.3', '8.0.0', '8.0.1', '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: class Pip:
def __init__(self, venv_dir): def __init__(self, venv_dir):
@ -28,6 +28,9 @@ def create_build_env(venv_dir='virtualenv'):
pip.install('six') pip.install('six')
# See: https://github.com/sphinx-doc/sphinx/issues/9777 # See: https://github.com/sphinx-doc/sphinx/issues/9777
pip.install('docutils==0.17.1') 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('sphinx-doc/sphinx', 'v3.3.0')
pip.install('michaeljones/breathe', 'v4.25.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_RVALUE_REFERENCES=1 \
FMT_USE_USER_DEFINED_LITERALS=1 \ FMT_USE_USER_DEFINED_LITERALS=1 \
FMT_USE_ALIAS_TEMPLATES=1 \ FMT_USE_ALIAS_TEMPLATES=1 \
FMT_USE_NONTYPE_TEMPLATE_ARGS=1 \
FMT_API= \ FMT_API= \
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \ "FMT_BEGIN_NAMESPACE=namespace fmt {{" \
"FMT_END_NAMESPACE=}}" \ "FMT_END_NAMESPACE=}}" \

View file

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

View file

@ -304,7 +304,8 @@ The available presentation types for pointers are:
Chrono Format Specifications Chrono Format Specifications
============================ ============================
Format specifications for chrono types have the following syntax: Format specifications for chrono types and ``std::tm`` have the following
syntax:
.. productionlist:: sf .. productionlist:: sf
chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`] chrono_format_spec: [[`fill`]`align`][`width`]["." `precision`][`chrono_specs`]
@ -345,12 +346,38 @@ points are:
| | command ``%OS`` produces the locale's alternative representation. | | | 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. are valid only for ``std::tm`` and not durations or time points.
``std::tm`` uses the system's `strftime .. range-specs:
<https://en.cppreference.com/w/cpp/chrono/c/strftime>`_ so refer to its
documentation for details on supported conversion specifiers. Range Format Specifications
===========================
Format specifications for range types have the following syntax:
..productionlist:: sf
range_format_spec: [":" [`underlying_spec`]]
The `underlying_spec` is parsed based on the formatter of the range's
reference type.
By default, a range of characters or strings is printed escaped and quoted. But
if any `underlying_spec` is provided (even if it is empty), then the characters
or strings are printed according to the provided specification.
Examples:
fmt::format("{}", std::vector{10, 20, 30});
// Result: [10, 20, 30]
fmt::format("{::#x}", std::vector{10, 20, 30});
// Result: [0xa, 0x14, 0x13]
fmt::format("{}", vector{'h', 'e', 'l', 'l', 'o'});
// Result: ['h', 'e', 'l', 'l', 'o']
fmt::format("{::}", vector{'h', 'e', 'l', 'l', 'o'});
// Result: [h, e, l, l, o]
fmt::format("{::d}", vector{'h', 'e', 'l', 'l', 'o'});
// Result: [104, 101, 108, 108, 111]
.. _formatexamples: .. _formatexamples:

View file

@ -95,10 +95,10 @@ class dynamic_format_arg_store
}; };
template <typename T> template <typename T>
using stored_type = conditional_t<detail::is_string<T>::value && using stored_type = conditional_t<
!has_formatter<T, Context>::value && std::is_convertible<T, std::basic_string<char_type>>::value &&
!detail::is_reference_wrapper<T>::value, !detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>; std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous. // Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_; std::vector<basic_format_arg<Context>> data_;

View file

@ -10,6 +10,8 @@
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <cmath> // std::isfinite
#include <cstring> // std::memcpy
#include <ctime> #include <ctime>
#include <iterator> #include <iterator>
#include <locale> #include <locale>
@ -321,14 +323,13 @@ constexpr const size_t codecvt_result<CodeUnit>::max_size;
template <typename CodeUnit> template <typename CodeUnit>
void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf, void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
const std::locale& loc) { const std::locale& loc) {
using codecvt = std::codecvt<CodeUnit, char, std::mbstate_t>;
#if FMT_CLANG_VERSION #if FMT_CLANG_VERSION
# pragma clang diagnostic push # pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated" # 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 # pragma clang diagnostic pop
#else #else
auto& f = std::use_facet<codecvt>(loc); auto& f = std::use_facet<std::codecvt<CodeUnit, char, std::mbstate_t>>(loc);
#endif #endif
auto mb = std::mbstate_t(); auto mb = std::mbstate_t();
const char* from_next = nullptr; 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()) { if (detail::is_utf8() && loc != get_classic_locale()) {
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
// gcc-4. // gcc-4.
#if FMT_MSC_VER != 0 || \ #if FMT_MSC_VERSION != 0 || \
(defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
// The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
// and newer. // and newer.
@ -468,7 +469,7 @@ inline std::tm localtime(std::time_t time) {
bool fallback(int res) { return res == 0; } bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER #if !FMT_MSC_VERSION
bool fallback(detail::null<>) { bool fallback(detail::null<>) {
using namespace fmt::detail; using namespace fmt::detail;
std::tm* tm = std::localtime(&time_); 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; } bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER #if !FMT_MSC_VERSION
bool fallback(detail::null<>) { bool fallback(detail::null<>) {
std::tm* tm = std::gmtime(&time_); std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm; 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; constexpr const size_t len = 8;
if (const_check(is_big_endian())) { if (const_check(is_big_endian())) {
char tmp[len]; char tmp[len];
memcpy(tmp, &digits, len); std::memcpy(tmp, &digits, len);
std::reverse_copy(tmp, tmp + len, buf); std::reverse_copy(tmp, tmp + len, buf);
} else { } 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]; char buf[10];
size_t offset = 0; size_t offset = 0;
if (year >= 0 && year < 10000) { if (year >= 0 && year < 10000) {
copy2(buf, digits2(to_unsigned(year / 100))); copy2(buf, digits2(static_cast<size_t>(year / 100)));
} else { } else {
offset = 4; offset = 4;
write_year_extended(year); 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() {} 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)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline bool isfinite(T) { inline bool isfinite(T) {
return true; return true;
@ -1470,14 +1462,22 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
#endif #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 // C++20 spec. If more than 18 fractional digits are required then returns 6 for
// microseconds precision. // microseconds precision.
constexpr int count_fractional_digits(long long num, long long den, int n = 0) { template <long long Num, long long Den, int N = 0,
return num % den == 0 bool Enabled = (N < 19) && (Num <= max_value<long long>() / 10)>
? n struct count_fractional_digits {
: (n > 18 ? 6 : count_fractional_digits(num * 10, den, n + 1)); 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) { constexpr long long pow10(std::uint32_t n) {
return n == 0 ? 1 : 10 * pow10(n - 1); 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; 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 = 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< using subsecond_precision = std::chrono::duration<
typename std::common_type<typename Duration::rep, typename std::common_type<typename Duration::rep,
@ -1674,12 +1676,9 @@ struct chrono_formatter {
if (std::ratio_less<typename subsecond_precision::period, if (std::ratio_less<typename subsecond_precision::period,
std::chrono::seconds::period>::value) { std::chrono::seconds::period>::value) {
*out++ = '.'; *out++ = '.';
// Don't convert long double to integer seconds to avoid overflow. auto fractional =
using sec = conditional_t< detail::abs(d) - std::chrono::duration_cast<std::chrono::seconds>(d);
std::is_same<typename Duration::rep, long double>::value, auto subseconds =
std::chrono::duration<long double>, std::chrono::seconds>;
auto fractional = detail::abs(d) - std::chrono::duration_cast<sec>(d);
const auto subseconds =
std::chrono::treat_as_floating_point< std::chrono::treat_as_floating_point<
typename subsecond_precision::rep>::value typename subsecond_precision::rep>::value
? fractional.count() ? fractional.count()
@ -1770,8 +1769,22 @@ struct chrono_formatter {
if (handle_nan_inf()) return; if (handle_nan_inf()) return;
if (ns == numeric_system::standard) { if (ns == numeric_system::standard) {
write(second(), 2); if (std::is_floating_point<rep>::value) {
write_fractional_seconds(std::chrono::duration<rep, Period>{val}); 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));
}
return; return;
} }
auto time = tm(); auto time = tm();

View file

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

View file

@ -13,45 +13,6 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
// An output iterator that counts the number of objects written to it and
// discards them.
class counting_iterator {
private:
size_t count_;
public:
using iterator_category = std::output_iterator_tag;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type = counting_iterator; // Mark iterator as checked.
struct value_type {
template <typename T> void operator=(const T&) {}
};
counting_iterator() : count_(0) {}
size_t count() const { return count_; }
counting_iterator& operator++() {
++count_;
return *this;
}
counting_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
friend counting_iterator operator+(counting_iterator it, difference_type n) {
it.count_ += static_cast<size_t>(n);
return it;
}
value_type operator*() const { return {}; }
};
template <typename Char, typename InputIt> template <typename Char, typename InputIt>
inline counting_iterator copy_str(InputIt begin, InputIt end, inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) { counting_iterator it) {
@ -75,8 +36,7 @@ template <typename OutputIt> class truncating_iterator_base {
using difference_type = std::ptrdiff_t; using difference_type = std::ptrdiff_t;
using pointer = void; using pointer = void;
using reference = void; using reference = void;
using _Unchecked_type = FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
truncating_iterator_base; // Mark iterator as checked.
OutputIt base() const { return out_; } OutputIt base() const { return out_; }
size_t count() const { return count_; } 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) # define FMT_COMPILE(s) FMT_STRING(s)
#endif #endif
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename Char, size_t N, template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str> fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string { struct udl_compiled_string : compiled_string {
using char_type = Char; using char_type = Char;
constexpr operator basic_string_view<char_type>() const { explicit constexpr operator basic_string_view<char_type>() const {
return {Str.data, N - 1}; return {Str.data, N - 1};
} }
}; };
@ -377,7 +337,8 @@ template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) { size_t pos, int next_arg_id) {
str.remove_prefix(pos); 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 f = formatter<T, Char>();
auto end = f.parse(ctx); auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1, 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()); constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) { detail::unknown_format>()) {
return format(static_cast<basic_string_view<typename S::char_type>>(S()), return fmt::format(
std::forward<Args>(args)...); static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else { } else {
return format(compiled, std::forward<Args>(args)...); return fmt::format(compiled, std::forward<Args>(args)...);
} }
} }
@ -586,11 +548,11 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S()); constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>, if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) { detail::unknown_format>()) {
return format_to(out, return fmt::format_to(
static_cast<basic_string_view<typename S::char_type>>(S()), out, static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...); std::forward<Args>(args)...);
} else { } else {
return format_to(out, compiled, std::forward<Args>(args)...); return fmt::format_to(out, compiled, std::forward<Args>(args)...);
} }
} }
#endif #endif
@ -599,22 +561,23 @@ template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const S& format_str, Args&&... args) { const S& format_str, Args&&... args) {
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str, auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
std::forward<Args>(args)...); format_str, std::forward<Args>(args)...);
return {it.base(), it.count()}; return {it.base(), it.count()};
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
size_t formatted_size(const S& format_str, const Args&... args) { size_t formatted_size(const S& format_str, const Args&... args) {
return format_to(detail::counting_iterator(), format_str, args...).count(); return fmt::format_to(detail::counting_iterator(), format_str, args...)
.count();
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) { void print(std::FILE* f, const S& format_str, const Args&... args) {
memory_buffer buffer; memory_buffer buffer;
format_to(std::back_inserter(buffer), format_str, args...); fmt::format_to(std::back_inserter(buffer), format_str, args...);
detail::print(f, {buffer.data(), buffer.size()}); detail::print(f, {buffer.data(), buffer.size()});
} }
@ -624,14 +587,12 @@ void print(const S& format_str, const Args&... args) {
print(stdout, format_str, args...); print(stdout, format_str, args...);
} }
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals { inline namespace literals {
template <detail_exported::fixed_string Str> template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
constexpr detail::udl_compiled_string< using char_t = remove_cvref_t<decltype(Str.data[0])>;
remove_cvref_t<decltype(Str.data[0])>, return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str> Str>();
operator""_cf() {
return {};
} }
} // namespace literals } // namespace literals
#endif #endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,2 +0,0 @@
#include "xchar.h"
#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead

View file

@ -9,10 +9,8 @@
#define FMT_OS_H_ #define FMT_OS_H_
#include <cerrno> #include <cerrno>
#include <clocale> // locale_t
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstdlib> // strtod_l
#include <system_error> // std::system_error #include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__) #if defined __APPLE__ || defined(__FreeBSD__)
@ -141,7 +139,7 @@ template <typename Char> struct formatter<std::error_code, Char> {
}; };
#ifdef _WIN32 #ifdef _WIN32
FMT_API const std::error_category& system_category() FMT_NOEXCEPT; FMT_API const std::error_category& system_category() noexcept;
FMT_BEGIN_DETAIL_NAMESPACE FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8. // A converter from UTF-16 to UTF-8.
@ -165,7 +163,7 @@ class utf16_to_utf8 {
}; };
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) FMT_NOEXCEPT; const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
FMT_API std::system_error vwindows_error(int error_code, string_view format_str, FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
@ -207,10 +205,9 @@ std::system_error windows_error(int error_code, string_view message,
// Reports a Windows error without throwing an exception. // Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors. // Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, FMT_API void report_windows_error(int error_code, const char* message) noexcept;
const char* message) FMT_NOEXCEPT;
#else #else
inline const std::error_category& system_category() FMT_NOEXCEPT { inline const std::error_category& system_category() noexcept {
return std::system_category(); return std::system_category();
} }
#endif // _WIN32 #endif // _WIN32
@ -237,13 +234,13 @@ class buffered_file {
void operator=(const buffered_file&) = delete; void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file. // Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {} buffered_file() noexcept : file_(nullptr) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT; FMT_API ~buffered_file() noexcept;
public: public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) { buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr; other.file_ = nullptr;
} }
@ -261,11 +258,9 @@ class buffered_file {
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; } FILE* get() const noexcept { return file_; }
// We place parentheses around fileno to workaround a bug in some versions FMT_API int descriptor() const;
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) { void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args); fmt::vprint(file_, format_str, args);
@ -279,12 +274,12 @@ class buffered_file {
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1. // A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw // Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as // fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather // closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the // than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler. // invalid parameter handler with _set_invalid_parameter_handler.
class file { class FMT_API file {
private: private:
int fd_; // File descriptor. int fd_; // File descriptor.
@ -303,16 +298,16 @@ class file {
}; };
// Constructs a file object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {} file() noexcept : fd_(-1) {}
// Opens a file and constructs a file object representing this file. // Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag); file(cstring_view path, int oflag);
public: public:
file(const file&) = delete; file(const file&) = delete;
void operator=(const file&) = delete; void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw. // Move assignment is not noexcept because close may throw.
file& operator=(file&& other) { file& operator=(file&& other) {
@ -323,43 +318,43 @@ class file {
} }
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT; ~file() noexcept;
// Returns the file descriptor. // Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; } int descriptor() const noexcept { return fd_; }
// Closes the file. // Closes the file.
FMT_API void close(); void close();
// Returns the file size. The size has signed type for consistency with // Returns the file size. The size has signed type for consistency with
// stat::st_size. // stat::st_size.
FMT_API long long size() const; long long size() const;
// Attempts to read count bytes from the file into the specified buffer. // Attempts to read count bytes from the file into the specified buffer.
FMT_API size_t read(void* buffer, size_t count); size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file. // Attempts to write count bytes from the specified buffer to the file.
FMT_API size_t write(const void* buffer, size_t count); size_t write(const void* buffer, size_t count);
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
FMT_API static file dup(int fd); static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd); void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT; void dup2(int fd, std::error_code& ec) noexcept;
// Creates a pipe setting up read_end and write_end file objects for reading // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end); static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // this file object from the file.
FMT_API buffered_file fdopen(const char* mode); buffered_file fdopen(const char* mode);
}; };
// Returns the memory page size. // Returns the memory page size.
@ -462,7 +457,7 @@ class FMT_API ostream final : private detail::buffer<char> {
* ``<integer>``: Flags passed to `open * ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ <https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE`` by default) (``file::WRONLY | file::CREATE | file::TRUNC`` by default)
* ``buffer_size=<integer>``: Output buffer size * ``buffer_size=<integer>``: Output buffer size
**Example**:: **Example**::
@ -477,50 +472,6 @@ inline ostream output_file(cstring_view path, T... params) {
} }
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
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_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE

View file

@ -8,6 +8,7 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include <fstream>
#include <ostream> #include <ostream>
#include "format.h" #include "format.h"
@ -45,15 +46,59 @@ struct is_streamable<
enable_if_t< enable_if_t<
std::is_arithmetic<T>::value || std::is_array<T>::value || std::is_arithmetic<T>::value || std::is_array<T>::value ||
std::is_pointer<T>::value || std::is_same<T, char8_type>::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_same<T, std_string_view<Char>>::value ||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>> (std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
: std::false_type {}; : 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. // Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing. // It is a separate function rather than a part of vprint to simplify testing.
template <typename Char> template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
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(); const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size(); unsigned_streamsize size = buf.size();
@ -76,38 +121,65 @@ void format_value(buffer<Char>& buf, const T& value,
#endif #endif
output << value; output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.try_resize(buf.size());
} }
// Formats an object of type T that has an overloaded ostream operator<<. template <typename T> struct streamed_view { const T& value; };
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 OutputIt> } // namespace detail
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
// 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 { -> OutputIt {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale()); format_value(buffer, value, ctx.locale());
return formatter<basic_string_view<Char>, Char>::format( return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx); {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> template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) auto format(detail::streamed_view<T> view,
-> OutputIt { basic_format_context<OutputIt, char>& ctx) const -> OutputIt {
auto buffer = basic_memory_buffer<Char>(); return ostream_formatter::format(view.value, ctx);
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
} }
}; };
/**
\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 } // namespace detail
FMT_MODULE_EXPORT FMT_MODULE_EXPORT template <typename Char>
template <typename Char> void vprint(std::basic_ostream<Char>& os,
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, basic_string_view<type_identity_t<Char>> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args); 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"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
FMT_MODULE_EXPORT FMT_MODULE_EXPORT template <typename... T>
template <typename S, typename... Args, void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> vprint(os, fmt, fmt::make_format_args(args...));
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... 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 FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_ #endif // FMT_OSTREAM_H_

View file

@ -10,7 +10,6 @@
#include <algorithm> // std::max #include <algorithm> // std::max
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include <ostream>
#include "format.h" #include "format.h"
@ -561,7 +560,7 @@ inline auto vsprintf(
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(fmt), args); vprintf(buffer, detail::to_string_view(fmt), args);
return to_string(buffer); 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>>> 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> { inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
using context = basic_printf_context_t<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>> 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) basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int { -> int {
basic_memory_buffer<Char> buffer; 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(); size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1 ? -1
@ -606,7 +606,7 @@ inline auto vfprintf(
template <typename S, typename... T, typename Char = char_t<S>> template <typename S, typename... T, typename Char = char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
using context = basic_printf_context_t<Char>; 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...)); fmt::make_format_args<context>(args...));
} }
@ -615,7 +615,7 @@ inline auto vprintf(
const S& fmt, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int { -> 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)> template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
inline auto printf(const S& fmt, const T&... args) -> int { inline auto printf(const S& fmt, const T&... args) -> int {
return vprintf( return vprintf(
to_string_view(fmt), detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...)); 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_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE

View file

@ -55,7 +55,7 @@ template <typename T> class is_std_string_like {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static constexpr const bool value =
is_string<T>::value || is_string<T>::value ||
std::is_convertible<T, std_string_view<char>>::value || std::is_convertible<T, std_string_view<char>>::value ||
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
@ -70,9 +70,9 @@ template <typename T> class is_map {
public: public:
#ifdef FMT_FORMAT_MAP_AS_LIST #ifdef FMT_FORMAT_MAP_AS_LIST
static FMT_CONSTEXPR_DECL const bool value = false; static constexpr const bool value = false;
#else #else
static FMT_CONSTEXPR_DECL const bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
#endif #endif
}; };
@ -83,9 +83,9 @@ template <typename T> class is_set {
public: public:
#ifdef FMT_FORMAT_SET_AS_LIST #ifdef FMT_FORMAT_SET_AS_LIST
static FMT_CONSTEXPR_DECL const bool value = false; static constexpr const bool value = false;
#else #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; !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
#endif #endif
}; };
@ -94,7 +94,7 @@ template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {}; template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800 #if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
# define FMT_DECLTYPE_RETURN(val) \ # define FMT_DECLTYPE_RETURN(val) \
->decltype(val) { return val; } \ ->decltype(val) { return val; } \
@ -174,12 +174,12 @@ template <typename T> class is_tuple_like_ {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
// Check for integer_sequence // 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> template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>; using integer_sequence = std::integer_sequence<T, N...>;
template <size_t... N> using index_sequence = std::index_sequence<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>; using make_index_sequence = make_integer_sequence<size_t, N>;
#endif #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> template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT { void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept {
using std::get; using std::get;
// using free function get<I>(T) now. // using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
@ -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)); 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> template <typename Range>
using value_type = using range_reference_type =
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>; decltype(*detail::range_begin(std::declval<Range&>()));
#endif
// We don't use the Range's value_type for anything, but we do need the Range's
// reference type, with cv-ref stripped.
template <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
template <typename 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) { template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
*out++ = ','; *out++ = ',';
@ -231,286 +283,9 @@ template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
return 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> template <typename Char, typename OutputIt>
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt { auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
*out++ = '"'; return write_escaped_string(out, str);
auto begin = str.begin(), end = str.end();
do {
auto escape = find_escape(begin, end);
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;
} }
template <typename Char, typename OutputIt, typename T, 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, template <typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)> FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg v) { OutputIt write_range_entry(OutputIt out, const Arg v) {
*out++ = '\''; return write_escaped_char(out, v);
*out++ = v;
*out++ = '\'';
return out;
} }
template < template <
@ -540,12 +312,19 @@ OutputIt write_range_entry(OutputIt out, const Arg& v) {
} // namespace detail } // namespace detail
template <typename T> struct is_tuple_like { 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; 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> 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: private:
// C++11 generic lambda for format(). // C++11 generic lambda for format().
template <typename FormatContext> struct format_each { template <typename FormatContext> struct format_each {
@ -565,7 +344,8 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
} }
template <typename FormatContext = format_context> template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const TupleT& values, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
*out++ = '('; *out++ = '(';
detail::for_each(values, format_each<FormatContext>{0, 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 { 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_range_<T>::value && !detail::is_std_string_like<T>::value &&
!detail::is_map<T>::value && !detail::is_map<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value && !std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<detail::std_string_view<Char>, T>::value; !std::is_constructible<detail::std_string_view<Char>, T>::value;
}; };
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< struct formatter<
T, Char, R, Char,
enable_if_t< enable_if_t<
fmt::is_range<T, Char>::value conjunction<fmt::is_range<R, Char>
// Workaround a bug in MSVC 2019 and earlier. // Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920
&& (is_formattable<detail::value_type<T>, Char>::value || ,
detail::has_fallback_formatter<detail::value_type<T>, Char>::value) 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 #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> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 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 < template <typename FormatContext>
typename FormatContext, typename U, auto format(range_type& range, FormatContext& ctx) const
FMT_ENABLE_IF( -> decltype(ctx.out()) {
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value, Char prefix = detail::is_set<R>::value ? '{' : '[';
const T, T>>::value)> Char postfix = detail::is_set<R>::value ? '}' : ']';
auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) { detail::range_mapper<buffer_context<Char>> mapper;
#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
auto out = ctx.out(); auto out = ctx.out();
*out++ = prefix; *out++ = prefix;
int i = 0; int i = 0;
auto it = std::begin(range); auto it = detail::range_begin(range);
auto end = std::end(range); auto end = detail::range_end(range);
for (; it != end; ++it) { for (; it != end; ++it) {
if (i > 0) out = detail::write_delimiter(out); if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, *it); if (custom_specs_) {
ctx.advance_to(out);
out = underlying_.format(mapper.map(*it), ctx);
} else {
out = detail::write_range_entry<Char>(out, *it);
}
++i; ++i;
} }
*out++ = postfix; *out++ = postfix;
@ -629,14 +460,21 @@ struct formatter<
template <typename T, typename Char> template <typename T, typename Char>
struct formatter< struct formatter<
T, Char, T, Char,
enable_if_t< enable_if_t<conjunction<detail::is_map<T>
detail::is_map<T>::value // Workaround a bug in MSVC 2017 and earlier.
// Workaround a bug in MSVC 2019 and earlier. #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920
#if !FMT_MSC_VER ,
&& (is_formattable<detail::value_type<T>, Char>::value || disjunction<
detail::has_fallback_formatter<detail::value_type<T>, Char>::value) 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 #endif
>> { >::value
>> {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
@ -647,7 +485,7 @@ struct formatter<
FMT_ENABLE_IF( FMT_ENABLE_IF(
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value, std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
const T, T>>::value)> const T, T>>::value)>
auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) { auto format(U& map, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
*out++ = '{'; *out++ = '{';
int i = 0; int i = 0;

176
include/fmt/std.h Normal file
View 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_

View file

@ -47,12 +47,7 @@ constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
} }
inline namespace literals { inline namespace literals {
constexpr auto operator"" _format(const wchar_t* s, size_t n) #if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
-> detail::udl_formatter<wchar_t> {
return {{s, n}};
}
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) { constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
return {s}; return {s};
} }
@ -87,13 +82,23 @@ auto vformat(basic_string_view<Char> format_str,
return to_string(buffer); 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 // Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size. // std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)> FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> { auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); return vformat(detail::to_string_view(format_str),
return vformat(to_string_view(format_str), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename Locale, typename S, typename Char = char_t<S>, template <typename Locale, typename S, typename Char = char_t<S>,
@ -103,7 +108,7 @@ inline auto vformat(
const Locale& loc, const S& format_str, const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> 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, template <typename Locale, typename S, typename... Args,
@ -112,8 +117,8 @@ template <typename Locale, typename S, typename... Args,
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, Args&&... args) inline auto format(const Locale& loc, const S& format_str, Args&&... args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), return detail::vformat(loc, detail::to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename OutputIt, typename S, typename Char = char_t<S>, template <typename OutputIt, typename S, typename Char = char_t<S>,
@ -123,7 +128,7 @@ auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt { -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); 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); 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&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); return vformat_to(out, detail::to_string_view(fmt),
return vformat_to(out, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
}
template <typename S, typename... Args, typename Char, size_t SIZE,
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);
} }
template <typename Locale, typename S, typename OutputIt, typename... 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, OutputIt out, const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); 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); return detail::get_iterator(buf);
} }
@ -167,8 +163,8 @@ template <
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) -> Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...); return vformat_to(out, loc, to_string_view(format_str),
return vformat_to(out, loc, to_string_view(format_str), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename OutputIt, typename Char, typename... Args, template <typename OutputIt, typename Char, typename... Args,
@ -190,16 +186,16 @@ template <typename OutputIt, typename S, typename... Args,
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
const Args&... args) -> format_to_n_result<OutputIt> { const Args&... args) -> format_to_n_result<OutputIt> {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); return vformat_to_n(out, n, detail::to_string_view(fmt),
return vformat_to_n(out, n, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
} }
template <typename S, typename... Args, typename Char = char_t<S>, template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t { inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
detail::counting_buffer<Char> buf; detail::counting_buffer<Char> buf;
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...); detail::vformat_to(buf, detail::to_string_view(fmt),
detail::vformat_to(buf, to_string_view(fmt), vargs); fmt::make_format_args<buffer_context<Char>>(args...));
return buf.count(); return buf.count();
} }

View file

@ -10,115 +10,38 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
// DEPRECATED! template FMT_API auto dragonbox::to_decimal(float x) noexcept
template <typename T = void> struct basic_data { -> dragonbox::decimal_fp<float>;
FMT_API static constexpr const char digits[100][2] = { template FMT_API auto dragonbox::to_decimal(double x) noexcept
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, -> dragonbox::decimal_fp<double>;
{'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;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const; template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif #endif
// Explicit instantiations for char. // 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>; -> 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! // DEPRECATED!
// There is no correspondent extern template in format.h because of // There is no correspondent extern template in format.h because of
// incompatibility between clang and gcc (#2377). // incompatibility between clang and gcc (#2377).
template FMT_API void detail::vformat_to( template FMT_API void vformat_to(buffer<char>&, string_view,
detail::buffer<char>&, string_view, basic_format_args<FMT_BUFFER_CONTEXT(char)>,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref); 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>&);
// Explicit instantiations for wchar_t. // 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>; -> 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*, template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
const wchar_t*);
template struct detail::basic_data<void>;
} // namespace detail
FMT_END_NAMESPACE FMT_END_NAMESPACE

View file

@ -35,9 +35,15 @@
# ifndef S_IRGRP # ifndef S_IRGRP
# define S_IRGRP 0 # define S_IRGRP 0
# endif # endif
# ifndef S_IWGRP
# define S_IWGRP 0
# endif
# ifndef S_IROTH # ifndef S_IROTH
# define S_IROTH 0 # define S_IROTH 0
# endif # endif
# ifndef S_IWOTH
# define S_IWOTH 0
# endif
# endif // _WIN32 # endif // _WIN32
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
@ -45,10 +51,6 @@
# include <windows.h> # include <windows.h>
#endif #endif
#ifdef fileno
# undef fileno
#endif
namespace { namespace {
#ifdef _WIN32 #ifdef _WIN32
// Return type of read and write functions. // Return type of read and write functions.
@ -107,7 +109,7 @@ class system_message {
unsigned long result_; unsigned long result_;
wchar_t* message_; wchar_t* message_;
static bool is_whitespace(wchar_t c) FMT_NOEXCEPT { static bool is_whitespace(wchar_t c) noexcept {
return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0'; return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
} }
@ -126,15 +128,15 @@ class system_message {
} }
} }
~system_message() { LocalFree(message_); } ~system_message() { LocalFree(message_); }
explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; } explicit operator bool() const noexcept { return result_ != 0; }
operator basic_string_view<wchar_t>() const FMT_NOEXCEPT { operator basic_string_view<wchar_t>() const noexcept {
return basic_string_view<wchar_t>(message_, result_); return basic_string_view<wchar_t>(message_, result_);
} }
}; };
class utf8_system_category final : public std::error_category { class utf8_system_category final : public std::error_category {
public: public:
const char* name() const FMT_NOEXCEPT override { return "system"; } const char* name() const noexcept override { return "system"; }
std::string message(int error_code) const override { std::string message(int error_code) const override {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
@ -149,7 +151,7 @@ class utf8_system_category final : public std::error_category {
} // namespace detail } // namespace detail
FMT_API const std::error_category& system_category() FMT_NOEXCEPT { FMT_API const std::error_category& system_category() noexcept {
static const detail::utf8_system_category category; static const detail::utf8_system_category category;
return category; return category;
} }
@ -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, void detail::format_windows_error(detail::buffer<char>& out, int error_code,
const char* message) FMT_NOEXCEPT { const char* message) noexcept {
FMT_TRY { FMT_TRY {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
utf16_to_utf8 utf8_message; utf16_to_utf8 utf8_message;
if (utf8_message.convert(msg) == ERROR_SUCCESS) { 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; return;
} }
} }
@ -176,12 +178,12 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT { void report_windows_error(int error_code, const char* message) noexcept {
report_error(detail::format_windows_error, error_code, message); report_error(detail::format_windows_error, error_code, message);
} }
#endif // _WIN32 #endif // _WIN32
buffered_file::~buffered_file() FMT_NOEXCEPT { buffered_file::~buffered_file() noexcept {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0) if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file"); report_system_error(errno, "cannot close file");
} }
@ -200,11 +202,8 @@ void buffered_file::close() {
if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
} }
// A macro used to prevent expansion of fileno on broken versions of MinGW. int buffered_file::descriptor() const {
#define FMT_ARGS int fd = FMT_POSIX_CALL(fileno(file_));
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd; return fd;
} }
@ -214,7 +213,8 @@ file::file(cstring_view path, int oflag) {
# ifdef _WIN32 # ifdef _WIN32
using mode_t = int; using mode_t = int;
# endif # 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__) # if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1; fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
@ -225,7 +225,7 @@ file::file(cstring_view path, int oflag) {
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
} }
file::~file() FMT_NOEXCEPT { file::~file() noexcept {
// Don't retry close in case of EINTR! // Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
@ -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; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) ec = std::error_code(errno, std::generic_category()); if (result == -1) ec = std::error_code(errno, std::generic_category());

View file

@ -1 +1 @@
4.2.1 5.1.1

View file

@ -11,18 +11,17 @@ cc_library(
"include/fmt/color.h", "include/fmt/color.h",
"include/fmt/compile.h", "include/fmt/compile.h",
"include/fmt/core.h", "include/fmt/core.h",
"include/fmt/format.h",
"include/fmt/format-inl.h", "include/fmt/format-inl.h",
"include/fmt/locale.h", "include/fmt/format.h",
"include/fmt/os.h", "include/fmt/os.h",
"include/fmt/ostream.h", "include/fmt/ostream.h",
"include/fmt/printf.h", "include/fmt/printf.h",
"include/fmt/ranges.h", "include/fmt/ranges.h",
"include/fmt/std.h",
"include/fmt/xchar.h", "include/fmt/xchar.h",
], ],
includes = [ includes = [
"include", "include",
"src",
], ],
strip_include_prefix = "include", strip_include_prefix = "include",
visibility = ["//visibility:public"], visibility = ["//visibility:public"],

View file

@ -1,7 +1,11 @@
# C++14 feature support detection # C++14 feature support detection
include(CheckCXXSourceCompiles)
include(CheckCXXCompilerFlag) 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) if (NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
@ -9,35 +13,38 @@ endif()
message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}") message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}")
if (CMAKE_CXX_STANDARD EQUAL 20) if (CMAKE_CXX_STANDARD EQUAL 20)
check_cxx_compiler_flag(-std=c++20 has_std_20_flag) fmt_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++2a has_std_2a_flag)
if (has_std_20_flag) if (has_std_20_flag)
set(CXX_STANDARD_FLAG -std=c++20) set(CXX_STANDARD_FLAG -std=c++20)
elseif (has_std_2a_flag) elseif (has_std_2a_flag)
set(CXX_STANDARD_FLAG -std=c++2a) set(CXX_STANDARD_FLAG -std=c++2a)
endif () endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 17) elseif (CMAKE_CXX_STANDARD EQUAL 17)
check_cxx_compiler_flag(-std=c++17 has_std_17_flag) fmt_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++1z has_std_1z_flag)
if (has_std_17_flag) if (has_std_17_flag)
set(CXX_STANDARD_FLAG -std=c++17) set(CXX_STANDARD_FLAG -std=c++17)
elseif (has_std_1z_flag) elseif (has_std_1z_flag)
set(CXX_STANDARD_FLAG -std=c++1z) set(CXX_STANDARD_FLAG -std=c++1z)
endif () endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 14) elseif (CMAKE_CXX_STANDARD EQUAL 14)
check_cxx_compiler_flag(-std=c++14 has_std_14_flag) fmt_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++1y has_std_1y_flag)
if (has_std_14_flag) if (has_std_14_flag)
set(CXX_STANDARD_FLAG -std=c++14) set(CXX_STANDARD_FLAG -std=c++14)
elseif (has_std_1y_flag) elseif (has_std_1y_flag)
set(CXX_STANDARD_FLAG -std=c++1y) set(CXX_STANDARD_FLAG -std=c++1y)
endif () endif ()
elseif (CMAKE_CXX_STANDARD EQUAL 11) elseif (CMAKE_CXX_STANDARD EQUAL 11)
check_cxx_compiler_flag(-std=c++11 has_std_11_flag) fmt_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++0x has_std_0x_flag)
if (has_std_11_flag) if (has_std_11_flag)
set(CXX_STANDARD_FLAG -std=c++11) set(CXX_STANDARD_FLAG -std=c++11)
@ -45,26 +52,3 @@ elseif (CMAKE_CXX_STANDARD EQUAL 11)
set(CXX_STANDARD_FLAG -std=c++0x) set(CXX_STANDARD_FLAG -std=c++0x)
endif () endif ()
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 )

View file

@ -1,4 +1,7 @@
@PACKAGE_INIT@ @PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake) if (NOT TARGET fmt::fmt)
include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake)
endif ()
check_required_components(fmt) check_required_components(fmt)

View file

@ -171,7 +171,7 @@ def main():
normal1 = compress_normal(normal1) normal1 = compress_normal(normal1)
print("""\ 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(singletons0u, singletons0l, 'singletons0', 'singletons0_lower')
print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower') print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower')

View file

@ -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}) add_library(test-main STATIC ${TEST_MAIN_SRC})
target_include_directories(test-main PUBLIC target_include_directories(test-main PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>) $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>)
target_link_libraries(test-main gtest) target_link_libraries(test-main gtest fmt)
include(CheckCXXCompilerFlag)
function(add_fmt_executable name) function(add_fmt_executable name)
add_executable(${name} ${ARGN}) add_executable(${name} ${ARGN})
@ -79,6 +77,15 @@ endif()
add_fmt_test(printf-test) add_fmt_test(printf-test)
add_fmt_test(ranges-test ranges-odr-test.cc) add_fmt_test(ranges-test ranges-odr-test.cc)
add_fmt_test(scan-test) 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) add_fmt_test(unicode-test HEADER_ONLY)
if (MSVC) if (MSVC)
target_compile_options(unicode-test PRIVATE /utf-8) target_compile_options(unicode-test PRIVATE /utf-8)
@ -128,9 +135,6 @@ if (NOT MSVC_STATIC_RUNTIME)
if (FMT_PEDANTIC) if (FMT_PEDANTIC)
target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_compile_options(posix-mock-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif () endif ()
if (HAVE_STRTOD_L)
target_compile_definitions(posix-mock-test PRIVATE FMT_LOCALE)
endif ()
add_test(NAME posix-mock-test COMMAND posix-mock-test) add_test(NAME posix-mock-test COMMAND posix-mock-test)
add_fmt_test(os-test) add_fmt_test(os-test)
endif () endif ()
@ -148,9 +152,7 @@ if (FMT_PEDANTIC)
target_include_directories( target_include_directories(
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include) noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_options(noexception-test PRIVATE -fno-exceptions) target_compile_options(noexception-test PRIVATE -fno-exceptions)
if (FMT_PEDANTIC) target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
target_compile_options(noexception-test PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif ()
endif () endif ()
# Test that the library compiles without locale. # Test that the library compiles without locale.
@ -174,8 +176,8 @@ if (FMT_PEDANTIC AND NOT WIN32)
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
"-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}" "-DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}"
"-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}"
"-DFMT_DIR=${CMAKE_SOURCE_DIR}" "-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}"
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}") "-DFMT_DIR=${CMAKE_SOURCE_DIR}")
# Test if the targets are found from the build directory. # Test if the targets are found from the build directory.
add_test(find-package-test ${CMAKE_CTEST_COMMAND} add_test(find-package-test ${CMAKE_CTEST_COMMAND}

View file

@ -557,6 +557,9 @@ TEST(chrono_test, special_durations) {
"03:33"); "03:33");
EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}), EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}),
"03:33:20"); "03:33:20");
EXPECT_EQ("44.000000000000",
fmt::format("{:%S}", std::chrono::duration<float, std::pico>(
1.54213895E+26)));
} }
TEST(chrono_test, unsigned_duration) { TEST(chrono_test, unsigned_duration) {
@ -620,6 +623,10 @@ TEST(chrono_test, cpp20_duration_subsecond_support) {
// fixed precision, and print zeros even if there is no fractional part. // fixed precision, and print zeros even if there is no fractional part.
EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}), EXPECT_EQ(fmt::format("{:%S}", std::chrono::microseconds{7000000}),
"07.000000"); "07.000000");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 3>>(1)),
"00.333333");
EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<long long, std::ratio<1, 7>>(1)),
"00.142857");
} }
#endif // FMT_STATIC_THOUSANDS_SEPARATOR #endif // FMT_STATIC_THOUSANDS_SEPARATOR

View file

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

View file

@ -6,6 +6,8 @@ project(compile-error-test CXX)
set(fmt_headers " set(fmt_headers "
#include <fmt/format.h> #include <fmt/format.h>
#include <fmt/xchar.h> #include <fmt/xchar.h>
#include <fmt/ostream.h>
#include <iostream>
") ")
set(error_test_names "") set(error_test_names "")
@ -154,6 +156,28 @@ expect_compile(format-function-error "
fmt::format(\"{}\", f); fmt::format(\"{}\", f);
" ERROR) " 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 # Make sure that compiler features detected in the header
# match the features detected in CMake. # match the features detected in CMake.
if (SUPPORTS_USER_DEFINED_LITERALS) if (SUPPORTS_USER_DEFINED_LITERALS)
@ -181,16 +205,30 @@ if (CMAKE_CXX_STANDARD GREATER_EQUAL 20)
#error #error
#endif #endif
" ERROR) " 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 # Compile-time argument name check
expect_compile(format-string-name " 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; using namespace fmt::literals;
fmt::print(\"{foo}\", \"foo\"_a=42); fmt::print(\"{foo}\", \"foo\"_a=42);
#endif #endif
") ")
expect_compile(format-string-name-error " 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; using namespace fmt::literals;
fmt::print(\"{foo}\", \"bar\"_a=42); fmt::print(\"{foo}\", \"bar\"_a=42);
#else #else

View file

@ -11,7 +11,7 @@
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 && \ #if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806 && \
defined(__cpp_constexpr) && __cpp_constexpr >= 201907 && \ defined(__cpp_constexpr) && __cpp_constexpr >= 201907 && \
defined(__cpp_constexpr_dynamic_alloc) && \ 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 <size_t max_string_length, typename Char = char> struct test_string {
template <typename T> constexpr bool operator==(const T& rhs) const noexcept { template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0; return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;

View file

@ -187,7 +187,7 @@ TEST(compile_test, named) {
EXPECT_THROW(fmt::format(FMT_COMPILE("{invalid}"), fmt::arg("valid", 42)), EXPECT_THROW(fmt::format(FMT_COMPILE("{invalid}"), fmt::arg("valid", 42)),
fmt::format_error); fmt::format_error);
# if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS # if FMT_USE_NONTYPE_TEMPLATE_ARGS
using namespace fmt::literals; using namespace fmt::literals;
auto statically_named_field_compiled = auto statically_named_field_compiled =
fmt::detail::compile<decltype("arg"_a = 42)>(FMT_COMPILE("{arg}")); fmt::detail::compile<decltype("arg"_a = 42)>(FMT_COMPILE("{arg}"));
@ -201,6 +201,11 @@ TEST(compile_test, named) {
# endif # endif
} }
TEST(compile_test, join) {
unsigned char data[] = {0x1, 0x2, 0xaf};
EXPECT_EQ("0102af", fmt::format(FMT_COMPILE("{:02x}"), fmt::join(data, "")));
}
TEST(compile_test, format_to) { TEST(compile_test, format_to) {
char buf[8]; char buf[8];
auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42); auto end = fmt::format_to(buf, FMT_COMPILE("{}"), 42);
@ -280,7 +285,7 @@ TEST(compile_test, print) {
} }
#endif #endif
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
TEST(compile_test, compile_format_string_literal) { TEST(compile_test, compile_format_string_literal) {
using namespace fmt::literals; using namespace fmt::literals;
EXPECT_EQ("", fmt::format(""_cf)); EXPECT_EQ("", fmt::format(""_cf));
@ -289,8 +294,15 @@ TEST(compile_test, compile_format_string_literal) {
} }
#endif #endif
#if __cplusplus >= 202002L || \ // MSVS 2019 19.29.30145.0 - Support C++20 and OK.
(__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) // 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 <size_t max_string_length, typename Char = char> struct test_string {
template <typename T> constexpr bool operator==(const T& rhs) const noexcept { template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0; return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;

View file

@ -128,7 +128,7 @@ TEST(core_test, buffer_appender) {
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470 #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
TEST(buffer_test, noncopyable) { TEST(buffer_test, noncopyable) {
EXPECT_FALSE(std::is_copy_constructible<buffer<char>>::value); 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. // std::is_copy_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value); EXPECT_FALSE(std::is_copy_assignable<buffer<char>>::value);
# endif # endif
@ -136,7 +136,7 @@ TEST(buffer_test, noncopyable) {
TEST(buffer_test, nonmoveable) { TEST(buffer_test, nonmoveable) {
EXPECT_FALSE(std::is_move_constructible<buffer<char>>::value); 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. // std::is_move_assignable is broken in MSVC2013.
EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value); EXPECT_FALSE(std::is_move_assignable<buffer<char>>::value);
# endif # endif
@ -385,11 +385,11 @@ VISIT_TYPE(unsigned long, unsigned long long);
template <typename T> class numeric_arg_test : public testing::Test {}; 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, testing::Types<bool, signed char, unsigned char, short, unsigned short, int,
unsigned, long, unsigned long, long long, unsigned long long, unsigned, long, unsigned long, long long, unsigned long long,
float, double, long double>; 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> template <typename T, fmt::enable_if_t<std::is_integral<T>::value, int> = 0>
T test_value() { T test_value() {
@ -679,6 +679,7 @@ TEST(format_test, constexpr_parse_format_string) {
#endif // FMT_USE_CONSTEXPR #endif // FMT_USE_CONSTEXPR
struct enabled_formatter {}; struct enabled_formatter {};
struct enabled_ptr_formatter {};
struct disabled_formatter {}; struct disabled_formatter {};
struct disabled_formatter_convertible { struct disabled_formatter_convertible {
operator int() const { return 42; } operator int() const { return 42; }
@ -693,6 +694,16 @@ template <> struct formatter<enabled_formatter> {
return ctx.out(); 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 FMT_END_NAMESPACE
TEST(core_test, has_formatter) { TEST(core_test, has_formatter) {
@ -737,7 +748,34 @@ struct convertible_to_pointer {
operator const int*() const { return nullptr; } 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) { TEST(core_test, is_formattable) {
#if 0 #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<fmt::basic_string_view<wchar_t>>::value,
""); "");
static_assert(fmt::is_formattable<enabled_formatter>::value, ""); static_assert(fmt::is_formattable<enabled_formatter>::value, "");
static_assert(!fmt::is_formattable<enabled_ptr_formatter*>::value, "");
static_assert(!fmt::is_formattable<disabled_formatter>::value, ""); static_assert(!fmt::is_formattable<disabled_formatter>::value, "");
static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, ""); static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, "");
@ -765,19 +804,22 @@ TEST(core_test, is_formattable) {
static_assert(fmt::is_formattable<const const_formattable&>::value, ""); static_assert(fmt::is_formattable<const const_formattable&>::value, "");
static_assert(fmt::is_formattable<nonconst_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, ""); static_assert(!fmt::is_formattable<const nonconst_formattable&>::value, "");
#endif #endif
static_assert(!fmt::is_formattable<convertible_to_pointer>::value, ""); 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, ""); static_assert(!fmt::is_formattable<void (*)()>::value, "");
struct s; struct s;
static_assert(!fmt::is_formattable<int(s::*)>::value, ""); static_assert(!fmt::is_formattable<int(s::*)>::value, "");
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"); } TEST(core_test, format) { EXPECT_EQ(fmt::format("{}", 42), "42"); }
@ -788,6 +830,10 @@ TEST(core_test, format_to) {
EXPECT_EQ(s, "42"); EXPECT_EQ(s, "42");
} }
TEST(core_test, format_as) {
EXPECT_EQ(fmt::format("{}", test::formattable_scoped_enum()), "42");
}
struct convertible_to_int { struct convertible_to_int {
operator int() const { return 42; } operator int() const { return 42; }
}; };
@ -850,13 +896,16 @@ TEST(core_test, format_implicitly_convertible_to_string_view) {
} }
// std::is_constructible is broken in MSVC until version 2015. // 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 { struct explicitly_convertible_to_string_view {
explicit operator fmt::string_view() const { return "foo"; } explicit operator fmt::string_view() const { return "foo"; }
}; };
TEST(core_test, format_explicitly_convertible_to_string_view) { TEST(core_test, format_explicitly_convertible_to_string_view) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view())); // Types explicitly convertible to string_view are not formattable by
// default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_string_view>::value, "");
} }
# ifdef FMT_USE_STRING_VIEW # ifdef FMT_USE_STRING_VIEW
@ -865,8 +914,11 @@ struct explicitly_convertible_to_std_string_view {
}; };
TEST(core_test, format_explicitly_convertible_to_std_string_view) { TEST(core_test, format_explicitly_convertible_to_std_string_view) {
EXPECT_EQ("foo", // Types explicitly convertible to string_view are not formattable by
fmt::format("{}", explicitly_convertible_to_std_string_view())); // default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_std_string_view>::value,
"");
} }
# endif # endif
#endif #endif

18
test/detect-stdfs.cc Normal file
View 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

View file

@ -24,9 +24,9 @@ static_assert(!std::is_copy_constructible<bigint>::value, "");
static_assert(!std::is_copy_assignable<bigint>::value, ""); static_assert(!std::is_copy_assignable<bigint>::value, "");
TEST(bigint_test, construct) { TEST(bigint_test, construct) {
EXPECT_EQ("", fmt::format("{}", bigint())); EXPECT_EQ(fmt::to_string(bigint()), "");
EXPECT_EQ("42", fmt::format("{}", bigint(0x42))); EXPECT_EQ(fmt::to_string(bigint(0x42)), "42");
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0))); EXPECT_EQ(fmt::to_string(bigint(0x123456789abcedf0)), "123456789abcedf0");
} }
TEST(bigint_test, compare) { TEST(bigint_test, compare) {
@ -72,63 +72,56 @@ TEST(bigint_test, add_compare) {
TEST(bigint_test, shift_left) { TEST(bigint_test, shift_left) {
bigint n(0x42); bigint n(0x42);
n <<= 0; n <<= 0;
EXPECT_EQ("42", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "42");
n <<= 1; n <<= 1;
EXPECT_EQ("84", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "84");
n <<= 25; n <<= 25;
EXPECT_EQ("108000000", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "108000000");
} }
TEST(bigint_test, multiply) { TEST(bigint_test, multiply) {
bigint n(0x42); bigint n(0x42);
EXPECT_THROW(n *= 0, assertion_failure); EXPECT_THROW(n *= 0, assertion_failure);
n *= 1; n *= 1;
EXPECT_EQ("42", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "42");
n *= 2; n *= 2;
EXPECT_EQ("84", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "84");
n *= 0x12345678; n *= 0x12345678;
EXPECT_EQ("962fc95e0", fmt::format("{}", n)); EXPECT_EQ(fmt::to_string(n), "962fc95e0");
bigint bigmax(max_value<uint32_t>()); bigint bigmax(max_value<uint32_t>());
bigmax *= max_value<uint32_t>(); bigmax *= max_value<uint32_t>();
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax)); EXPECT_EQ(fmt::to_string(bigmax), "fffffffe00000001");
bigmax.assign(max_value<uint64_t>());
bigmax *= max_value<uint64_t>();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
}
TEST(bigint_test, accumulator) { const auto max64 = max_value<uint64_t>();
fmt::detail::accumulator acc; bigmax = max64;
EXPECT_EQ(acc.lower, 0); bigmax *= max64;
EXPECT_EQ(acc.upper, 0); EXPECT_EQ(fmt::to_string(bigmax), "fffffffffffffffe0000000000000001");
acc.upper = 12;
acc.lower = 34; const auto max128 = (fmt::detail::uint128_t(max64) << 64) | max64;
EXPECT_EQ(static_cast<uint32_t>(acc), 34); bigmax = max128;
acc += 56; bigmax *= max128;
EXPECT_EQ(acc.lower, 90); EXPECT_EQ(fmt::to_string(bigmax),
acc += max_value<uint64_t>(); "fffffffffffffffffffffffffffffffe00000000000000000000000000000001");
EXPECT_EQ(acc.upper, 13);
EXPECT_EQ(acc.lower, 89);
acc >>= 32;
EXPECT_EQ(acc.upper, 0);
EXPECT_EQ(acc.lower, 13 * 0x100000000);
} }
TEST(bigint_test, square) { TEST(bigint_test, square) {
bigint n0(0); bigint n0(0);
n0.square(); n0.square();
EXPECT_EQ("0", fmt::format("{}", n0)); EXPECT_EQ(fmt::to_string(n0), "0");
bigint n1(0x100); bigint n1(0x100);
n1.square(); n1.square();
EXPECT_EQ("10000", fmt::format("{}", n1)); EXPECT_EQ(fmt::to_string(n1), "10000");
bigint n2(0xfffffffff); bigint n2(0xfffffffff);
n2.square(); n2.square();
EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2)); EXPECT_EQ(fmt::to_string(n2), "ffffffffe000000001");
bigint n3(max_value<uint64_t>()); bigint n3(max_value<uint64_t>());
n3.square(); n3.square();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3)); EXPECT_EQ(fmt::to_string(n3), "fffffffffffffffe0000000000000001");
bigint n4; bigint n4;
n4.assign_pow10(10); n4.assign_pow10(10);
EXPECT_EQ("2540be400", fmt::format("{}", n4)); EXPECT_EQ(fmt::to_string(n4), "2540be400");
} }
TEST(bigint_test, divmod_assign_zero_divisor) { TEST(bigint_test, divmod_assign_zero_divisor) {
@ -150,8 +143,8 @@ TEST(bigint_test, divmod_assign_unaligned) {
n2.assign_pow10(100); n2.assign_pow10(100);
int result = n1.divmod_assign(n2); int result = n1.divmod_assign(n2);
EXPECT_EQ(result, 9406); EXPECT_EQ(result, 9406);
EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96", EXPECT_EQ(fmt::to_string(n1),
fmt::format("{}", n1)); "10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96");
} }
TEST(bigint_test, divmod_assign) { TEST(bigint_test, divmod_assign) {
@ -159,19 +152,19 @@ TEST(bigint_test, divmod_assign) {
bigint n1(100); bigint n1(100);
int result = n1.divmod_assign(bigint(10)); int result = n1.divmod_assign(bigint(10));
EXPECT_EQ(result, 10); EXPECT_EQ(result, 10);
EXPECT_EQ("0", fmt::format("{}", n1)); EXPECT_EQ(fmt::to_string(n1), "0");
// pow(10, 100) / (42 << 320): // pow(10, 100) / (42 << 320):
n1.assign_pow10(100); n1.assign_pow10(100);
result = n1.divmod_assign(bigint(42) <<= 320); result = n1.divmod_assign(bigint(42) <<= 320);
EXPECT_EQ(result, 111); EXPECT_EQ(result, 111);
EXPECT_EQ("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96", EXPECT_EQ(fmt::to_string(n1),
fmt::format("{}", n1)); "13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96");
// 42 / 100: // 42 / 100:
bigint n2(42); bigint n2(42);
n1.assign_pow10(2); n1.assign_pow10(2);
result = n2.divmod_assign(n1); result = n2.divmod_assign(n1);
EXPECT_EQ(result, 0); 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() { template <bool is_iec559> void run_double_tests() {
@ -190,8 +183,8 @@ TEST(fp_test, double_tests) {
TEST(fp_test, normalize) { TEST(fp_test, normalize) {
const auto v = fp(0xbeef, 42); const auto v = fp(0xbeef, 42);
auto normalized = normalize(v); auto normalized = normalize(v);
EXPECT_EQ(0xbeef000000000000, normalized.f); EXPECT_EQ(normalized.f, 0xbeef000000000000);
EXPECT_EQ(-6, normalized.e); EXPECT_EQ(normalized.e, -6);
} }
TEST(fp_test, multiply) { TEST(fp_test, multiply) {
@ -207,18 +200,18 @@ TEST(fp_test, get_cached_power) {
using limits = std::numeric_limits<double>; using limits = std::numeric_limits<double>;
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) { for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
int dec_exp = 0; int dec_exp = 0;
auto fp = fmt::detail::get_cached_power(exp, dec_exp); auto power = fmt::detail::get_cached_power(exp, dec_exp);
bigint exact, cache(fp.f); bigint exact, cache(power.f);
if (dec_exp >= 0) { if (dec_exp >= 0) {
exact.assign_pow10(dec_exp); exact.assign_pow10(dec_exp);
if (fp.e <= 0) if (power.e <= 0)
exact <<= -fp.e; exact <<= -power.e;
else else
cache <<= fp.e; cache <<= power.e;
exact.align(cache); exact.align(cache);
cache.align(exact); cache.align(exact);
auto exact_str = fmt::format("{}", exact); auto exact_str = fmt::to_string(exact);
auto cache_str = fmt::format("{}", cache); auto cache_str = fmt::to_string(cache);
EXPECT_EQ(exact_str.size(), cache_str.size()); EXPECT_EQ(exact_str.size(), cache_str.size());
EXPECT_EQ(exact_str.substr(0, 15), cache_str.substr(0, 15)); EXPECT_EQ(exact_str.substr(0, 15), cache_str.substr(0, 15));
int diff = cache_str[15] - exact_str[15]; int diff = cache_str[15] - exact_str[15];
@ -228,12 +221,12 @@ TEST(fp_test, get_cached_power) {
EXPECT_EQ(diff, 0); EXPECT_EQ(diff, 0);
} else { } else {
cache.assign_pow10(-dec_exp); cache.assign_pow10(-dec_exp);
cache *= fp.f + 1; // Inexact check. cache *= power.f + 1; // Inexact check.
exact.assign(1); exact = 1;
exact <<= -fp.e; exact <<= -power.e;
exact.align(cache); exact.align(cache);
auto exact_str = fmt::format("{}", exact); auto exact_str = fmt::to_string(exact);
auto cache_str = fmt::format("{}", cache); auto cache_str = fmt::to_string(cache);
EXPECT_EQ(exact_str.size(), cache_str.size()); EXPECT_EQ(exact_str.size(), cache_str.size());
EXPECT_EQ(exact_str.substr(0, 16), cache_str.substr(0, 16)); 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) { TEST(fp_test, dragonbox_max_k) {
using fmt::detail::dragonbox::floor_log10_pow2; using fmt::detail::dragonbox::floor_log10_pow2;
using float_info = fmt::detail::dragonbox::float_info<float>; using float_info = fmt::detail::dragonbox::float_info<float>;
EXPECT_EQ(fmt::detail::const_check(float_info::max_k), EXPECT_EQ(
float_info::kappa - floor_log10_pow2(float_info::min_exponent - fmt::detail::const_check(float_info::max_k),
float_info::significand_bits)); float_info::kappa -
floor_log10_pow2(std::numeric_limits<float>::min_exponent -
fmt::detail::num_significand_bits<float>() - 1));
using double_info = fmt::detail::dragonbox::float_info<double>; using double_info = fmt::detail::dragonbox::float_info<double>;
EXPECT_EQ( EXPECT_EQ(
fmt::detail::const_check(double_info::max_k), fmt::detail::const_check(double_info::max_k),
double_info::kappa - floor_log10_pow2(double_info::min_exponent - double_info::kappa -
double_info::significand_bits)); floor_log10_pow2(std::numeric_limits<double>::min_exponent -
fmt::detail::num_significand_bits<double>() - 1));
} }
TEST(fp_test, get_round_direction) { TEST(fp_test, get_round_direction) {
using fmt::detail::get_round_direction; using fmt::detail::get_round_direction;
using fmt::detail::round_direction; using fmt::detail::round_direction;
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0)); EXPECT_EQ(get_round_direction(100, 50, 0), round_direction::down);
EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0)); EXPECT_EQ(get_round_direction(100, 51, 0), round_direction::up);
EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10)); EXPECT_EQ(get_round_direction(100, 40, 10), round_direction::down);
EXPECT_EQ(round_direction::up, get_round_direction(100, 60, 10)); EXPECT_EQ(get_round_direction(100, 60, 10), round_direction::up);
for (size_t i = 41; i < 60; ++i) 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>(); uint64_t max = max_value<uint64_t>();
EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure); 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, 100), assertion_failure);
EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure); EXPECT_THROW(get_round_direction(100, 0, 50), assertion_failure);
// Check that remainder + error doesn't overflow. // 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. // Check that 2 * (remainder + error) doesn't overflow.
EXPECT_EQ(round_direction::unknown, EXPECT_EQ(get_round_direction(max, max / 2 + 1, max / 2),
get_round_direction(max, max / 2 + 1, max / 2)); round_direction::unknown);
// Check that remainder - error doesn't overflow. // 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. // 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) { TEST(fp_test, fixed_handler) {
@ -297,20 +293,20 @@ TEST(fp_test, fixed_handler) {
} }
TEST(fp_test, grisu_format_compiles_with_on_ieee_double) { 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); format_float(0.42, -1, fmt::detail::float_specs(), buf);
} }
TEST(format_impl_test, format_error_code) { TEST(format_impl_test, format_error_code) {
std::string msg = "error 42", sep = ": "; std::string msg = "error 42", sep = ": ";
{ {
fmt::memory_buffer buffer; auto buffer = fmt::memory_buffer();
format_to(fmt::appender(buffer), "garbage"); format_to(fmt::appender(buffer), "garbage");
fmt::detail::format_error_code(buffer, 42, "test"); 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 = auto prefix =
std::string(fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x'); std::string(fmt::inline_buffer_size - msg.size() - sep.size() + 1, 'x');
fmt::detail::format_error_code(buffer, 42, prefix); 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. // Test with a message that doesn't fit into the buffer.
prefix += 'x'; prefix += 'x';
fmt::detail::format_error_code(buffer, codes[i], prefix); 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 = 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) { for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
n *= 10; n *= 10;
EXPECT_EQ(i, fmt::detail::count_digits(n - 1)); EXPECT_EQ(fmt::detail::count_digits(n - 1), i);
EXPECT_EQ(i + 1, fmt::detail::count_digits(n)); 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_count_digits<uint64_t>();
} }
TEST(format_impl_test, write_fallback_uintptr) { #if FMT_USE_FLOAT128
std::string s; TEST(format_impl_test, write_float128) {
fmt::detail::write_ptr<char>( auto s = std::string();
std::back_inserter(s), fmt::detail::write<char>(std::back_inserter(s), __float128(42));
fmt::detail::fallback_uintptr(reinterpret_cast<void*>(0xface)), nullptr); EXPECT_EQ(s, "42");
EXPECT_EQ(s, "0xface"); }
#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 #ifdef _WIN32
# include <windows.h> # include <windows.h>
#endif
#ifdef _WIN32
TEST(format_impl_test, write_console_signature) { TEST(format_impl_test, write_console_signature) {
decltype(WriteConsoleW)* p = fmt::detail::WriteConsoleW; decltype(::WriteConsoleW)* p = fmt::detail::WriteConsoleW;
(void)p; (void)p;
} }
#endif #endif

View file

@ -33,12 +33,98 @@ using fmt::memory_buffer;
using fmt::runtime; using fmt::runtime;
using fmt::string_view; using fmt::string_view;
using fmt::detail::max_value; using fmt::detail::max_value;
using fmt::detail::uint128_fallback;
using testing::Return; using testing::Return;
using testing::StrictMock; using testing::StrictMock;
enum { buffer_size = 256 }; 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 { struct uint32_pair {
uint32_t u[2]; uint32_t u[2];
}; };
@ -223,8 +309,9 @@ TEST(memory_buffer_test, move_ctor_dynamic_buffer) {
buffer.push_back('a'); buffer.push_back('a');
basic_memory_buffer<char, 4, std_allocator> buffer2(std::move(buffer)); basic_memory_buffer<char, 4, std_allocator> buffer2(std::move(buffer));
// Move should rip the guts of the first buffer. // Move should rip the guts of the first buffer.
EXPECT_EQ(inline_buffer_ptr, &buffer[0]); EXPECT_EQ(&buffer[0], inline_buffer_ptr);
EXPECT_EQ("testa", std::string(&buffer2[0], buffer2.size())); EXPECT_EQ(buffer.size(), 0);
EXPECT_EQ(std::string(&buffer2[0], buffer2.size()), "testa");
EXPECT_GT(buffer2.capacity(), 4u); EXPECT_GT(buffer2.capacity(), 4u);
} }
@ -325,7 +412,7 @@ template <typename Allocator, size_t MaxSize>
class max_size_allocator : public Allocator { class max_size_allocator : public Allocator {
public: public:
using typename Allocator::value_type; using typename Allocator::value_type;
size_t max_size() const FMT_NOEXCEPT { return MaxSize; } size_t max_size() const noexcept { return MaxSize; }
value_type* allocate(size_t n) { value_type* allocate(size_t n) {
if (n > max_size()) { if (n > max_size()) {
throw std::length_error("size > max_size"); throw std::length_error("size > max_size");
@ -570,6 +657,9 @@ TEST(format_test, plus_sign) {
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("+42", fmt::format("{0:+}", 42ll)); EXPECT_EQ("+42", fmt::format("{0:+}", 42ll));
#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, EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ull), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("+42", fmt::format("{0:+}", 42.0)); EXPECT_EQ("+42", fmt::format("{0:+}", 42.0));
@ -918,6 +1008,14 @@ TEST(format_test, precision) {
EXPECT_THAT(outputs, EXPECT_THAT(outputs,
testing::Contains(fmt::format("{:.838A}", -2.14001164E+38))); testing::Contains(fmt::format("{:.838A}", -2.14001164E+38)));
if (std::numeric_limits<long double>::digits == 64) {
auto ld = (std::numeric_limits<long double>::min)();
EXPECT_EQ(fmt::format("{:.0}", ld), "3e-4932");
EXPECT_EQ(
fmt::format("{:0g}", std::numeric_limits<long double>::denorm_min()),
"3.6452e-4951");
}
EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0)); EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0));
EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234)); EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234));
EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001)); EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001));
@ -939,6 +1037,7 @@ TEST(format_test, precision) {
format_error, "number is too big"); format_error, "number is too big");
EXPECT_EQ("st", fmt::format("{0:.2}", "str")); EXPECT_EQ("st", fmt::format("{0:.2}", "str"));
EXPECT_EQ("вожык", fmt::format("{0:.5}", "вожыкі"));
} }
TEST(format_test, runtime_precision) { TEST(format_test, runtime_precision) {
@ -1060,7 +1159,7 @@ TEST(format_test, format_short) {
template <typename T> template <typename T>
void check_unknown_types(const T& value, const char* types, const char*) { void check_unknown_types(const T& value, const char* types, const char*) {
char format_str[buffer_size]; char format_str[buffer_size];
const char* special = ".0123456789L}"; const char* special = ".0123456789L?}";
for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) { for (int i = CHAR_MIN; i <= CHAR_MAX; ++i) {
char c = static_cast<char>(i); char c = static_cast<char>(i);
if (std::strchr(types, c) || std::strchr(special, c) || !c) continue; if (std::strchr(types, c) || std::strchr(special, c) || !c) continue;
@ -1229,32 +1328,30 @@ TEST(format_test, format_float) {
} }
TEST(format_test, format_double) { TEST(format_test, format_double) {
EXPECT_EQ("0", fmt::format("{}", 0.0)); EXPECT_EQ(fmt::format("{}", 0.0), "0");
check_unknown_types(1.2, "eEfFgGaAnL%", "double"); check_unknown_types(1.2, "eEfFgGaAnL%", "double");
EXPECT_EQ("0", fmt::format("{:}", 0.0)); EXPECT_EQ(fmt::format("{:}", 0.0), "0");
EXPECT_EQ("0.000000", fmt::format("{:f}", 0.0)); EXPECT_EQ(fmt::format("{:f}", 0.0), "0.000000");
EXPECT_EQ("0", fmt::format("{:g}", 0.0)); EXPECT_EQ(fmt::format("{:g}", 0.0), "0");
EXPECT_EQ("392.65", fmt::format("{:}", 392.65)); EXPECT_EQ(fmt::format("{:}", 392.65), "392.65");
EXPECT_EQ("392.65", fmt::format("{:g}", 392.65)); EXPECT_EQ(fmt::format("{:g}", 392.65), "392.65");
EXPECT_EQ("392.65", fmt::format("{:G}", 392.65)); EXPECT_EQ(fmt::format("{:G}", 392.65), "392.65");
EXPECT_EQ("4.9014e+06", fmt::format("{:g}", 4.9014e6)); EXPECT_EQ(fmt::format("{:g}", 4.9014e6), "4.9014e+06");
EXPECT_EQ("392.650000", fmt::format("{:f}", 392.65)); EXPECT_EQ(fmt::format("{:f}", 392.65), "392.650000");
EXPECT_EQ("392.650000", fmt::format("{:F}", 392.65)); EXPECT_EQ(fmt::format("{:F}", 392.65), "392.650000");
EXPECT_EQ("42", fmt::format("{:L}", 42.0)); EXPECT_EQ(fmt::format("{:L}", 42.0), "42");
EXPECT_EQ(" 0x1.0cccccccccccdp+2", fmt::format("{:24a}", 4.2)); EXPECT_EQ(fmt::format("{:24a}", 4.2), " 0x1.0cccccccccccdp+2");
EXPECT_EQ("0x1.0cccccccccccdp+2 ", fmt::format("{:<24a}", 4.2)); EXPECT_EQ(fmt::format("{:<24a}", 4.2), "0x1.0cccccccccccdp+2 ");
EXPECT_EQ(fmt::format("{0:e}", 392.65), "3.926500e+02");
EXPECT_EQ(fmt::format("{0:E}", 392.65), "3.926500E+02");
EXPECT_EQ(fmt::format("{0:+010.4g}", 392.65), "+0000392.6");
char buffer[buffer_size]; char buffer[buffer_size];
safe_sprintf(buffer, "%e", 392.65);
EXPECT_EQ(buffer, fmt::format("{0:e}", 392.65));
safe_sprintf(buffer, "%E", 392.65);
EXPECT_EQ(buffer, fmt::format("{0:E}", 392.65));
EXPECT_EQ("+0000392.6", fmt::format("{0:+010.4g}", 392.65));
safe_sprintf(buffer, "%a", -42.0); safe_sprintf(buffer, "%a", -42.0);
EXPECT_EQ(buffer, fmt::format("{:a}", -42.0)); EXPECT_EQ(fmt::format("{:a}", -42.0), buffer);
safe_sprintf(buffer, "%A", -42.0); safe_sprintf(buffer, "%A", -42.0);
EXPECT_EQ(buffer, fmt::format("{:A}", -42.0)); EXPECT_EQ(fmt::format("{:A}", -42.0), buffer);
EXPECT_EQ("9223372036854775808.000000", EXPECT_EQ(fmt::format("{:f}", 9223372036854775807.0),
fmt::format("{:f}", 9223372036854775807.0)); "9223372036854775808.000000");
} }
TEST(format_test, precision_rounding) { TEST(format_test, precision_rounding) {
@ -1363,6 +1460,9 @@ TEST(format_test, format_char) {
<< format_str; << format_str;
} }
EXPECT_EQ(fmt::format("{:02X}", n), fmt::format("{:02X}", 'x')); EXPECT_EQ(fmt::format("{:02X}", n), fmt::format("{:02X}", 'x'));
EXPECT_EQ("\n", fmt::format("{}", '\n'));
EXPECT_EQ("'\\n'", fmt::format("{:?}", '\n'));
} }
TEST(format_test, format_volatile_char) { TEST(format_test, format_volatile_char) {
@ -1393,7 +1493,11 @@ TEST(format_test, format_pointer) {
EXPECT_EQ("0x0", fmt::format("{0}", static_cast<void*>(nullptr))); 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}", reinterpret_cast<void*>(0x1234)));
EXPECT_EQ("0x1234", fmt::format("{0:p}", 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()))); fmt::format("{0}", reinterpret_cast<void*>(~uintptr_t())));
EXPECT_EQ("0x1234", EXPECT_EQ("0x1234",
fmt::format("{}", fmt::ptr(reinterpret_cast<int*>(0x1234)))); fmt::format("{}", fmt::ptr(reinterpret_cast<int*>(0x1234))));
@ -1409,14 +1513,43 @@ TEST(format_test, format_pointer) {
EXPECT_EQ("0x0", fmt::format("{}", nullptr)); EXPECT_EQ("0x0", fmt::format("{}", nullptr));
} }
TEST(format_test, write_uintptr_fallback) {
// Test that formatting a pointer by converting it to uint128_fallback works.
// This is needed to support systems without uintptr_t.
auto s = std::string();
fmt::detail::write_ptr<char>(
std::back_inserter(s),
fmt::detail::bit_cast<fmt::detail::uint128_fallback>(
reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ(s, "0xface");
}
enum class color { red, green, blue };
namespace test_ns {
enum class color { red, green, blue };
using fmt::enums::format_as;
} // namespace test_ns
TEST(format_test, format_enum_class) {
EXPECT_EQ(fmt::format("{}", fmt::underlying(color::red)), "0");
EXPECT_EQ(fmt::format("{}", test_ns::color::red), "0");
}
TEST(format_test, format_string) { TEST(format_test, format_string) {
EXPECT_EQ("test", fmt::format("{0}", std::string("test"))); EXPECT_EQ(fmt::format("{0}", std::string("test")), "test");
EXPECT_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")), EXPECT_THROW((void)fmt::format(fmt::runtime("{:x}"), std::string("test")),
fmt::format_error); fmt::format_error);
} }
TEST(format_test, format_string_view) { TEST(format_test, format_string_view) {
EXPECT_EQ("test", fmt::format("{}", string_view("test"))); EXPECT_EQ("test", fmt::format("{}", string_view("test")));
EXPECT_EQ("\"t\\nst\"", fmt::format("{:?}", string_view("t\nst")));
EXPECT_EQ("", fmt::format("{}", string_view())); EXPECT_EQ("", fmt::format("{}", string_view()));
} }
@ -1623,6 +1756,7 @@ TEST(format_test, group_digits_view) {
} }
enum test_enum { foo, bar }; enum test_enum { foo, bar };
auto format_as(test_enum e) -> int { return e; }
TEST(format_test, join) { TEST(format_test, join) {
using fmt::join; using fmt::join;
@ -1652,9 +1786,15 @@ TEST(format_test, join) {
} }
#ifdef __cpp_lib_byte #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) { TEST(format_test, join_bytes) {
auto v = std::vector<std::byte>{std::byte(1), std::byte(2), std::byte(3)}; 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 #endif
@ -1694,20 +1834,21 @@ fmt::string_view to_string_view(string_like) { return "foo"; }
constexpr char with_null[3] = {'{', '}', '\0'}; constexpr char with_null[3] = {'{', '}', '\0'};
constexpr char no_null[2] = {'{', '}'}; constexpr char no_null[2] = {'{', '}'};
static FMT_CONSTEXPR_DECL const char static_with_null[3] = {'{', '}', '\0'}; static constexpr const char static_with_null[3] = {'{', '}', '\0'};
static FMT_CONSTEXPR_DECL const char static_no_null[2] = {'{', '}'}; static constexpr const char static_no_null[2] = {'{', '}'};
TEST(format_test, compile_time_string) { TEST(format_test, compile_time_string) {
EXPECT_EQ("foo", fmt::format(FMT_STRING("foo"))); EXPECT_EQ("foo", fmt::format(FMT_STRING("foo")));
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like())); 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; using namespace fmt::literals;
EXPECT_EQ("foobar", fmt::format(FMT_STRING("{foo}{bar}"), "bar"_a = "bar", EXPECT_EQ("foobar", fmt::format(FMT_STRING("{foo}{bar}"), "bar"_a = "bar",
"foo"_a = "foo")); "foo"_a = "foo"));
EXPECT_EQ("", fmt::format(FMT_STRING(""))); EXPECT_EQ("", fmt::format(FMT_STRING("")));
EXPECT_EQ("", fmt::format(FMT_STRING(""), "arg"_a = 42)); EXPECT_EQ("", fmt::format(FMT_STRING(""), "arg"_a = 42));
EXPECT_EQ("42", fmt::format(FMT_STRING("{answer}"), "answer"_a = Answer()));
#endif #endif
(void)static_with_null; (void)static_with_null;
@ -1719,11 +1860,11 @@ TEST(format_test, compile_time_string) {
(void)with_null; (void)with_null;
(void)no_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(with_null), 42));
EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42)); EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42));
#endif #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)); EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42));
#endif #endif
} }
@ -1739,44 +1880,16 @@ TEST(format_test, custom_format_compile_time_string) {
} }
#if FMT_USE_USER_DEFINED_LITERALS #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) { TEST(format_test, named_arg_udl) {
using namespace fmt::literals;
auto udl_a = fmt::format("{first}{second}{first}{third}", "first"_a = "abra", auto udl_a = fmt::format("{first}{second}{first}{third}", "first"_a = "abra",
"second"_a = "cad", "third"_a = 99); "second"_a = "cad", "third"_a = 99);
EXPECT_EQ( EXPECT_EQ(
fmt::format("{first}{second}{first}{third}", fmt::arg("first", "abra"), fmt::format("{first}{second}{first}{third}", fmt::arg("first", "abra"),
fmt::arg("second", "cad"), fmt::arg("third", 99)), fmt::arg("second", "cad"), fmt::arg("third", 99)),
udl_a); udl_a);
EXPECT_EQ("42", fmt::format("{answer}", "answer"_a = Answer()));
} }
#endif // FMT_USE_USER_DEFINED_LITERALS #endif // FMT_USE_USER_DEFINED_LITERALS
@ -1790,6 +1903,7 @@ TEST(format_test, formatter_not_specialized) {
#if FMT_HAS_FEATURE(cxx_strong_enums) #if FMT_HAS_FEATURE(cxx_strong_enums)
enum big_enum : unsigned long long { big_enum_value = 5000000000ULL }; 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) { TEST(format_test, strong_enum) {
EXPECT_EQ("5000000000", fmt::format("{}", big_enum_value)); 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(reinterpret_cast<void*>(0x1234)), "0x1234");
EXPECT_EQ(fmt::to_string(adl_test::fmt::detail::foo()), "foo"); 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(convertible_to_int()), "42");
EXPECT_EQ(fmt::to_string(foo), "0");
enum foo : unsigned char { zero }; #if FMT_USE_FLOAT128
EXPECT_EQ(fmt::to_string(zero), "0"); EXPECT_EQ(fmt::to_string(__float128(0.5)), "0.5");
#endif
} }
TEST(format_test, output_iterators) { TEST(format_test, output_iterators) {
@ -2038,7 +2154,7 @@ TEST(format_test, format_string_errors) {
EXPECT_ERROR_NOARGS("foo", nullptr); EXPECT_ERROR_NOARGS("foo", nullptr);
EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string"); EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string");
EXPECT_ERROR("{0:s", "unknown format specifier", date); EXPECT_ERROR("{0:s", "unknown format specifier", date);
# if !FMT_MSC_VER || FMT_MSC_VER >= 1916 # if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1916
// This causes an detail compiler error in MSVC2017. // This causes an detail compiler error in MSVC2017.
EXPECT_ERROR("{:{<}", "invalid fill character '{'", int); EXPECT_ERROR("{:{<}", "invalid fill character '{'", int);
EXPECT_ERROR("{:10000000000}", "number is too big", int); EXPECT_ERROR("{:10000000000}", "number is too big", int);
@ -2070,7 +2186,8 @@ TEST(format_test, format_string_errors) {
# else # else
fmt::print("warning: constexpr is broken in this version of MSVC\n"); fmt::print("warning: constexpr is broken in this version of MSVC\n");
# endif # 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("bar"_a = 42));
EXPECT_ERROR("{foo}", "named argument is not found", EXPECT_ERROR("{foo}", "named argument is not found",
decltype(fmt::arg("foo", 42))); decltype(fmt::arg("foo", 42)));
@ -2116,7 +2233,7 @@ TEST(format_test, char_traits_is_not_ambiguous) {
using namespace std; using namespace std;
auto c = char_traits<char>::char_type(); auto c = char_traits<char>::char_type();
(void)c; (void)c;
#if __cplusplus >= 201103L #if FMT_CPLUSPLUS >= 201103L
auto s = std::string(); auto s = std::string();
auto lval = begin(s); auto lval = begin(s);
(void)lval; (void)lval;

View file

@ -30,8 +30,8 @@ void invoke_fmt(const uint8_t* data, size_t size) {
#if FMT_FUZZ_FORMAT_TO_STRING #if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(format_str.get(), *value); std::string message = fmt::format(format_str.get(), *value);
#else #else
fmt::memory_buffer message; auto buf = fmt::memory_buffer();
fmt::format_to(message, format_str.get(), *value); fmt::format_to(std::back_inserter(buf), format_str.get(), *value);
#endif #endif
} catch (std::exception&) { } catch (std::exception&) {
} }

View file

@ -27,8 +27,8 @@ void invoke_fmt(const uint8_t* data, size_t size) {
#if FMT_FUZZ_FORMAT_TO_STRING #if FMT_FUZZ_FORMAT_TO_STRING
std::string message = fmt::format(format_str, item1, item2); std::string message = fmt::format(format_str, item1, item2);
#else #else
fmt::memory_buffer message; auto buf = fmt::memory_buffer();
fmt::format_to(message, format_str, item1, item2); fmt::format_to(std::back_inserter(buf), format_str, item1, item2);
#endif #endif
} }

View file

@ -347,7 +347,7 @@ TEST(output_redirect_test, flush_error_in_ctor) {
TEST(output_redirect_test, dup_error_in_ctor) { TEST(output_redirect_test, dup_error_in_ctor) {
buffered_file f = open_buffered_file(); buffered_file f = open_buffered_file();
int fd = (f.fileno)(); int fd = (f.descriptor)();
file copy = file::dup(fd); file copy = file::dup(fd);
FMT_POSIX(close(fd)); FMT_POSIX(close(fd));
std::unique_ptr<output_redirect> redir{nullptr}; std::unique_ptr<output_redirect> redir{nullptr};

View file

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

View file

@ -83,7 +83,7 @@ class output_redirect {
public: public:
explicit output_redirect(FILE* file); explicit output_redirect(FILE* file);
~output_redirect() FMT_NOEXCEPT; ~output_redirect() noexcept;
output_redirect(const output_redirect&) = delete; output_redirect(const output_redirect&) = delete;
void operator=(const output_redirect&) = delete; void operator=(const output_redirect&) = delete;

View file

@ -18,7 +18,7 @@ else ()
endif () endif ()
# Workaround GTest bug https://github.com/google/googletest/issues/705. # 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) -fno-delete-null-pointer-checks HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
if (HAVE_FNO_DELETE_NULL_POINTER_CHECKS) if (HAVE_FNO_DELETE_NULL_POINTER_CHECKS)
target_compile_options(gtest PUBLIC -fno-delete-null-pointer-checks) target_compile_options(gtest PUBLIC -fno-delete-null-pointer-checks)

View file

@ -36,7 +36,6 @@
#else #else
# define FMT_USE_FCNTL 0 # define FMT_USE_FCNTL 0
#endif #endif
#define FMT_NOEXCEPT noexcept
#if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)
# define FMT_POSIX(call) _##call # define FMT_POSIX(call) _##call
#else #else
@ -196,13 +195,6 @@ TEST(module_test, wformat_args) {
EXPECT_TRUE(args.get(0)); EXPECT_TRUE(args.get(0));
} }
TEST(module_test, checked_format_args) {
fmt::basic_format_args args = fmt::make_args_checked<int>("{}", 42);
EXPECT_TRUE(args.get(0));
fmt::basic_format_args wargs = fmt::make_args_checked<int>(L"{}", 42);
EXPECT_TRUE(wargs.get(0));
}
TEST(module_test, dynamic_format_args) { TEST(module_test, dynamic_format_args) {
fmt::dynamic_format_arg_store<fmt::format_context> dyn_store; fmt::dynamic_format_arg_store<fmt::format_context> dyn_store;
dyn_store.push_back(fmt::arg("a42", 42)); dyn_store.push_back(fmt::arg("a42", 42));

View file

@ -14,10 +14,6 @@
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.h" #include "util.h"
#ifdef fileno
# undef fileno
#endif
using fmt::buffered_file; using fmt::buffered_file;
using testing::HasSubstr; using testing::HasSubstr;
using wstring_view = fmt::basic_string_view<wchar_t>; 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) { TEST(buffered_file_test, move_assignment_closes_file) {
buffered_file bf = open_buffered_file(); buffered_file bf = open_buffered_file();
buffered_file bf2 = open_buffered_file(); buffered_file bf2 = open_buffered_file();
int old_fd = bf2.fileno(); int old_fd = bf2.descriptor();
bf2 = std::move(bf); bf2 = std::move(bf);
EXPECT_TRUE(isclosed(old_fd)); 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) { TEST(buffered_file_test, move_from_temporary_in_assignment_closes_file) {
buffered_file f = open_buffered_file(); buffered_file f = open_buffered_file();
int old_fd = f.fileno(); int old_fd = f.descriptor();
f = open_buffered_file(); f = open_buffered_file();
EXPECT_TRUE(isclosed(old_fd)); EXPECT_TRUE(isclosed(old_fd));
} }
@ -234,7 +230,7 @@ TEST(buffered_file_test, close_file_in_dtor) {
int fd = 0; int fd = 0;
{ {
buffered_file f = open_buffered_file(); buffered_file f = open_buffered_file();
fd = f.fileno(); fd = f.descriptor();
} }
EXPECT_TRUE(isclosed(fd)); 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 // otherwise the system may recycle closed file descriptor when
// redirecting the output in EXPECT_STDERR and the second close // redirecting the output in EXPECT_STDERR and the second close
// will break output redirection. // will break output redirection.
FMT_POSIX(close(f->fileno())); FMT_POSIX(close(f->descriptor()));
SUPPRESS_ASSERT(f.reset(nullptr)); SUPPRESS_ASSERT(f.reset(nullptr));
}, },
system_error_message(EBADF, "cannot close file") + "\n"); 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) { TEST(buffered_file_test, close) {
buffered_file f = open_buffered_file(); buffered_file f = open_buffered_file();
int fd = f.fileno(); int fd = f.descriptor();
f.close(); f.close();
EXPECT_TRUE(f.get() == nullptr); EXPECT_TRUE(f.get() == nullptr);
EXPECT_TRUE(isclosed(fd)); EXPECT_TRUE(isclosed(fd));
@ -265,15 +261,15 @@ TEST(buffered_file_test, close) {
TEST(buffered_file_test, close_error) { TEST(buffered_file_test, close_error) {
buffered_file f = open_buffered_file(); 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_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
EXPECT_TRUE(f.get() == nullptr); EXPECT_TRUE(f.get() == nullptr);
} }
TEST(buffered_file_test, fileno) { TEST(buffered_file_test, descriptor) {
auto f = open_buffered_file(); auto f = open_buffered_file();
EXPECT_TRUE(f.fileno() != -1); EXPECT_TRUE(f.descriptor() != -1);
file copy = file::dup(f.fileno()); file copy = file::dup(f.descriptor());
EXPECT_READ(copy, file_content); EXPECT_READ(copy, file_content);
} }

View file

@ -5,6 +5,8 @@
// //
// For the license information refer to format.h. // For the license information refer to format.h.
#include <fstream>
#include "fmt/format.h" #include "fmt/format.h"
using fmt::runtime; using fmt::runtime;
@ -30,12 +32,12 @@ template <> struct formatter<test> : formatter<int> {
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.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(); os << d.year() << '-' << d.month() << '-' << d.day();
return os; 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(); os << d.year() << L'-' << d.month() << L'-' << d.day();
return os; return os;
} }
@ -47,11 +49,24 @@ template <typename T> type_with_comma_op operator<<(T&, const date&);
enum streamable_enum {}; enum streamable_enum {};
std::ostream& operator<<(std::ostream& os, streamable_enum) { auto operator<<(std::ostream& os, streamable_enum) -> std::ostream& {
return os << "streamable_enum"; return os << "streamable_enum";
} }
enum unstreamable_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) { TEST(ostream_test, enum) {
EXPECT_EQ("streamable_enum", fmt::format("{}", streamable_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)); EXPECT_EQ("te", fmt::format("{0:.{1}}", test_string("test"), 2));
} }
struct empty_test {};
std::ostream& operator<<(std::ostream& os, empty_test) { return os << ""; }
TEST(ostream_test, empty_custom_output) { TEST(ostream_test, empty_custom_output) {
EXPECT_EQ("", fmt::format("{}", empty_test())); EXPECT_EQ("", fmt::format("{}", empty_test()));
} }
@ -121,7 +133,7 @@ TEST(ostream_test, write_to_ostream_max_size) {
struct mock_streambuf : std::streambuf { struct mock_streambuf : std::streambuf {
MOCK_METHOD2(xsputn, std::streamsize(const void* s, std::streamsize n)); 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; const void* v = s;
return xsputn(v, n); return xsputn(v, n);
} }
@ -158,15 +170,16 @@ TEST(ostream_test, join_fallback_formatter) {
#if FMT_USE_CONSTEXPR #if FMT_USE_CONSTEXPR
TEST(ostream_test, constexpr_string) { TEST(ostream_test, constexpr_string) {
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42"))); EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), std::string("42")));
EXPECT_EQ("a string", format(FMT_STRING("{0}"), test_string("a string"))); EXPECT_EQ("a string",
fmt::format(FMT_STRING("{0}"), test_string("a string")));
} }
#endif #endif
namespace fmt_test { namespace fmt_test {
struct abc {}; struct abc {};
template <typename Output> Output& operator<<(Output& out, abc) { template <typename Output> auto operator<<(Output& out, abc) -> Output& {
return out << "abc"; return out << "abc";
} }
} // namespace fmt_test } // namespace fmt_test
@ -174,7 +187,7 @@ template <typename Output> Output& operator<<(Output& out, abc) {
template <typename T> struct test_template {}; template <typename T> struct test_template {};
template <typename T> 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; return os << 1;
} }
@ -184,6 +197,8 @@ template <typename T> struct formatter<test_template<T>> : formatter<int> {
return formatter<int>::format(2, ctx); return formatter<int>::format(2, ctx);
} }
}; };
template <> struct formatter<fmt_test::abc> : ostream_formatter {};
} // namespace fmt } // namespace fmt
TEST(ostream_test, template) { TEST(ostream_test, template) {
@ -214,41 +229,6 @@ TEST(ostream_test, disable_builtin_ostream_operators) {
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo"))); EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
} }
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_string_like) {
return os << "bar";
}
TEST(ostream_test, format_explicitly_convertible_to_string_like) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator fmt::detail::std_string_view<char>() const {
return {"foo", 3u};
}
};
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_std_string_view) {
return os << "bar";
}
TEST(ostream_test, format_explicitly_convertible_to_std_string_view) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#endif // FMT_USE_STRING_VIEW
struct streamable_and_convertible_to_bool { struct streamable_and_convertible_to_bool {
operator bool() const { return true; } operator bool() const { return true; }
}; };
@ -262,6 +242,21 @@ TEST(ostream_test, format_convertible_to_bool) {
EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_bool()), "true"); EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_bool()), "true");
} }
struct streamable_and_convertible_to_string_view {
operator fmt::string_view() const { return "foo"; }
};
std::ostream& operator<<(std::ostream& os,
streamable_and_convertible_to_string_view) {
return os << "bar";
}
TEST(ostream_test, format_convertible_to_string_vew) {
// operator<< is intentionally not used because of potential ODR violations.
EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_string_view()),
"foo");
}
struct copyfmt_test {}; struct copyfmt_test {};
std::ostream& operator<<(std::ostream& os, 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"; return os << "foo";
} }
namespace fmt {
template <> struct formatter<copyfmt_test> : ostream_formatter {};
} // namespace fmt
TEST(ostream_test, copyfmt) { TEST(ostream_test, copyfmt) {
EXPECT_EQ("foo", fmt::format("{}", copyfmt_test())); EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
} }
@ -286,11 +285,15 @@ TEST(ostream_test, range) {
struct abstract { struct abstract {
virtual ~abstract() = default; virtual ~abstract() = default;
virtual void f() = 0; 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; return os;
} }
}; };
namespace fmt {
template <> struct formatter<abstract> : ostream_formatter {};
} // namespace fmt
void format_abstract_compiles(const abstract& a) { void format_abstract_compiles(const abstract& a) {
fmt::format(FMT_COMPILE("{}"), 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<std::string>());
EXPECT_TRUE(fmt::is_formattable<fmt::detail::std_string_view<char>>()); 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");
}

View file

@ -6,7 +6,7 @@
// For the license information refer to format.h. // For the license information refer to format.h.
// Disable bogus MSVC warnings. // Disable bogus MSVC warnings.
#ifndef _CRT_SECURE_NO_WARNINGS #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS
#endif #endif
@ -438,7 +438,7 @@ TEST(buffered_file_test, fileno_no_retry) {
file::pipe(read_end, write_end); file::pipe(read_end, write_end);
buffered_file f = read_end.fdopen("r"); buffered_file f = read_end.fdopen("r");
fileno_count = 1; 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); EXPECT_EQ(2, fileno_count);
fileno_count = 0; fileno_count = 0;
} }
@ -457,84 +457,3 @@ TEST(scoped_mock, scope) {
} }
EXPECT_EQ(nullptr, test_mock::instance); EXPECT_EQ(nullptr, test_mock::instance);
} }
#ifdef FMT_LOCALE
using locale_type = fmt::locale::type;
struct locale_mock {
static locale_mock* instance;
MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale,
locale_type base));
MOCK_METHOD1(freelocale, void(locale_type locale));
} * 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

View file

@ -11,7 +11,6 @@
#include <climits> #include <climits>
#include <cstring> #include <cstring>
#include "fmt/ostream.h"
#include "fmt/xchar.h" #include "fmt/xchar.h"
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.h" #include "util.h"
@ -505,6 +504,7 @@ TEST(printf_test, pointer) {
} }
enum test_enum { answer = 42 }; enum test_enum { answer = 42 };
auto format_as(test_enum e) -> int { return e; }
TEST(printf_test, enum) { TEST(printf_test, enum) {
EXPECT_PRINTF("42", "%d", answer); EXPECT_PRINTF("42", "%d", answer);
@ -533,10 +533,6 @@ TEST(printf_test, wide_string) {
EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc"));
} }
TEST(printf_test, printf_custom) {
EXPECT_EQ("abc", test_sprintf("%s", test_string("abc")));
}
TEST(printf_test, vprintf) { TEST(printf_test, vprintf) {
fmt::format_arg_store<fmt::printf_context, int> as{42}; fmt::format_arg_store<fmt::printf_context, int> as{42};
fmt::basic_format_args<fmt::printf_context> args(as); fmt::basic_format_args<fmt::printf_context> args(as);

View file

@ -21,7 +21,7 @@
# define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY # define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
#endif #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_JOIN
# define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT # define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
#endif #endif
@ -46,11 +46,13 @@ TEST(ranges_test, format_array_of_literals) {
TEST(ranges_test, format_vector) { TEST(ranges_test, format_vector) {
auto v = std::vector<int>{1, 2, 3, 5, 7, 11}; auto v = std::vector<int>{1, 2, 3, 5, 7, 11};
EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]"); EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]");
EXPECT_EQ(fmt::format("{::#x}", v), "[0x1, 0x2, 0x3, 0x5, 0x7, 0xb]");
} }
TEST(ranges_test, format_vector2) { TEST(ranges_test, format_vector2) {
auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}}; auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}};
EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]"); EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]");
EXPECT_EQ(fmt::format("{:::#x}", v), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]");
} }
TEST(ranges_test, format_map) { TEST(ranges_test, format_map) {
@ -63,16 +65,42 @@ TEST(ranges_test, format_set) {
"{\"one\", \"two\"}"); "{\"one\", \"two\"}");
} }
namespace adl {
struct box {
int value;
};
auto begin(const box& b) -> const int* { return &b.value; }
auto end(const box& b) -> const int* { return &b.value + 1; }
} // namespace adl
TEST(ranges_test, format_adl_begin_end) {
auto b = adl::box{42};
EXPECT_EQ(fmt::format("{}", b), "[42]");
}
TEST(ranges_test, format_pair) { TEST(ranges_test, format_pair) {
auto p = std::pair<int, float>(42, 1.5f); auto p = std::pair<int, float>(42, 1.5f);
EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)"); EXPECT_EQ(fmt::format("{}", p), "(42, 1.5)");
} }
struct unformattable {};
TEST(ranges_test, format_tuple) { TEST(ranges_test, format_tuple) {
auto t = auto t =
std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i'); 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("{}", t), "(42, 1.5, \"this is tuple\", 'i')");
EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()"); 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 #ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
@ -131,8 +159,8 @@ TEST(ranges_test, path_like) {
struct string_like { struct string_like {
const char* begin(); const char* begin();
const char* end(); const char* end();
explicit operator fmt::string_view() const { return "foo"; } operator fmt::string_view() const { return "foo"; }
explicit operator std::string_view() const { return "foo"; } operator std::string_view() const { return "foo"; }
}; };
TEST(ranges_test, format_string_like) { TEST(ranges_test, format_string_like) {
@ -196,14 +224,14 @@ TEST(ranges_test, range) {
} }
enum test_enum { foo }; enum test_enum { foo };
auto format_as(test_enum e) -> int { return e; }
TEST(ranges_test, enum_range) { TEST(ranges_test, enum_range) {
auto v = std::vector<test_enum>{test_enum::foo}; auto v = std::vector<test_enum>{test_enum::foo};
EXPECT_EQ(fmt::format("{}", v), "[0]"); EXPECT_EQ(fmt::format("{}", v), "[0]");
} }
#if !FMT_MSC_VER #if !FMT_MSC_VERSION
struct unformattable {};
TEST(ranges_test, unformattable_range) { TEST(ranges_test, unformattable_range) {
EXPECT_FALSE((fmt::has_formatter<std::vector<unformattable>, 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) { TEST(ranges_test, join_sentinel) {
auto hello = zstring{"hello"}; auto hello = zstring{"hello"};
EXPECT_EQ(fmt::format("{}", hello), "['h', 'e', 'l', 'l', 'o']"); EXPECT_EQ(fmt::format("{}", hello), "['h', 'e', 'l', 'l', 'o']");
EXPECT_EQ(fmt::format("{::}", hello), "[h, e, l, l, o]");
EXPECT_EQ(fmt::format("{}", fmt::join(hello, "_")), "h_e_l_l_o"); EXPECT_EQ(fmt::format("{}", fmt::join(hello, "_")), "h_e_l_l_o");
} }
@ -348,6 +377,7 @@ TEST(ranges_test, escape_string) {
EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]"); EXPECT_EQ(fmt::format("{}", vec{"\xf0\xaa\x9b\x9e"}), "[\"\\U0002a6de\"]");
EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}), EXPECT_EQ(fmt::format("{}", vec{"\xf4\x8f\xbf\xc0"}),
"[\"\\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\"]"); "[\"foo\"]");
} }
#endif // FMT_USE_STRING_VIEW #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
View 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
}

View file

@ -15,9 +15,6 @@
#ifdef _MSC_VER #ifdef _MSC_VER
# include <crtdbg.h> # include <crtdbg.h>
#else
# define _CrtSetReportFile(a, b)
# define _CrtSetReportMode(a, b)
#endif #endif
int main(int argc, char** argv) { int main(int argc, char** argv) {
@ -28,11 +25,13 @@ int main(int argc, char** argv) {
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX |
SEM_NOOPENFILEERRORBOX); SEM_NOOPENFILEERRORBOX);
#endif #endif
#ifdef _MSC_VER
// Disable message boxes on assertion failures. // Disable message boxes on assertion failures.
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
#endif
try { try {
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
testing::FLAGS_gtest_death_test_style = "threadsafe"; testing::FLAGS_gtest_death_test_style = "threadsafe";

View file

@ -66,14 +66,16 @@ TYPED_TEST(is_string_test, is_string) {
} }
// std::is_constructible is broken in MSVC until version 2015. // 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 { struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; } explicit operator fmt::wstring_view() const { return L"foo"; }
}; };
TEST(xchar_test, format_explicitly_convertible_to_wstring_view) { TEST(xchar_test, format_explicitly_convertible_to_wstring_view) {
EXPECT_EQ(L"foo", // Types explicitly convertible to wstring_view are not formattable by
fmt::format(L"{}", explicitly_convertible_to_wstring_view())); // default because it may introduce ODR violations.
static_assert(
!fmt::is_formattable<explicitly_convertible_to_wstring_view>::value, "");
} }
#endif #endif
@ -96,12 +98,12 @@ TEST(xchar_test, is_formattable) {
} }
TEST(xchar_test, compile_time_string) { TEST(xchar_test, compile_time_string) {
#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L #if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L
EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42)); EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42));
#endif #endif
} }
#if __cplusplus > 201103L #if FMT_CPLUSPLUS > 201103L
struct custom_char { struct custom_char {
int value; int value;
custom_char() = default; custom_char() = default;
@ -183,11 +185,6 @@ TEST(format_test, wide_format_to_n) {
} }
#if FMT_USE_USER_DEFINED_LITERALS #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) { TEST(xchar_test, named_arg_udl) {
using namespace fmt::literals; using namespace fmt::literals;
auto udl_a = auto udl_a =
@ -218,7 +215,14 @@ std::wostream& operator<<(std::wostream& os, streamable_enum) {
return os << L"streamable_enum"; return os << L"streamable_enum";
} }
namespace fmt {
template <>
struct formatter<streamable_enum, wchar_t> : basic_ostream_formatter<wchar_t> {
};
} // namespace fmt
enum unstreamable_enum {}; enum unstreamable_enum {};
auto format_as(unstreamable_enum e) -> int { return e; }
TEST(xchar_test, enum) { TEST(xchar_test, enum) {
EXPECT_EQ(L"streamable_enum", fmt::format(L"{}", streamable_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); 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) { TEST(xchar_test, chrono) {
auto tm = std::tm(); auto tm = std::tm();
tm.tm_year = 116; tm.tm_year = 116;
@ -322,9 +304,22 @@ TEST(xchar_test, color) {
} }
TEST(xchar_test, ostream) { TEST(xchar_test, ostream) {
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409
std::wostringstream wos; std::wostringstream wos;
fmt::print(wos, L"Don't {}!", L"panic"); 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)); } 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) { TEST(locale_test, localized_double) {
auto loc = std::locale(std::locale(), new numpunct<char>()); auto loc = std::locale(std::locale(), new numpunct<char>());
EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23)); EXPECT_EQ(fmt::format(loc, "{:L}", 1.23), "1?23");
EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23)); EXPECT_EQ(fmt::format(loc, "{:Lf}", 1.23), "1?230000");
EXPECT_EQ("1~234?5", fmt::format(loc, "{:L}", 1234.5)); EXPECT_EQ(fmt::format(loc, "{:L}", 1234.5), "1~234?5");
EXPECT_EQ("12~000", fmt::format(loc, "{:L}", 12000.0)); EXPECT_EQ(fmt::format(loc, "{:L}", 12000.0), "12~000");
EXPECT_EQ(fmt::format(loc, "{:8L}", 1230.0), " 1~230");
} }
TEST(locale_test, format) { TEST(locale_test, format) {