Squashed 'external/fmt/' changes from 3b70966df..093b39ca5
093b39ca5 Update docs for meson (#4291) 2c3a5698e Simplify a copying the fill from basic_specs fc1b0f348 Clarify use of FMT_THROW in a comment 1d066890c Resolve C4702 unreachable code warnings dad323751 Fix a bug when copying the fill from basic_specs 880e1494d Improve xchar support for std::bitset formatter e3ddede6c Update version e9ec4fdc8 Bump version feb72126b Readd FMT_NO_UNIQUE_ADDRESS 8d517e54c Update changelog 563fc74ae Update changelog 3e04222d5 Restore ABI compatibility with 11.0.2 853df39d0 Mention compile-time formatting 11742a09c Clarify that format_string should be used instead of fstring da24fac10 Document fstring 5fa4bdd75 Define CMake components to allow docs to be installed separately (#4276) 3c8aad8df Update the release script 0e8aad961 Update version debe784aa Update changelog f6d112567 Update changelog 73d0d3f75 Fix github API call 08f60f1ef Update changelog faf3f8408 Bump version f3a41441d Replace requests with urllib 3f33cb21d Update changelog b07a90386 Update changelog a6fba5177 Update changelog 25e292998 Update changelog 00ab2e98b Update changelog a3ef285ae Always inline const_check to improve debug codegen in clang 28d1abc9d Update changelog 90704b9ef Update changelog 86dae01c2 Fix compatibility with older versions of VS (#4271) d8a79eafd Document formatting of bit-fields and fields of packed structs 7c3d0152e Use the _MSVC_STL_UPDATE macro to detect STL (#4267) 7c50da538 Allow getting size of dynamic format arg store (#4270) 873670ba3 Make parameter basic_memory_buffer<char, SIZE>& buf of to_string const 735d4cc05 Update changelog 141380172 Allow disabling <filesystem> by define FMT_CPP_LIB_FILESYSTEM=0 (#4259) 4302d7429 Update changelog 0f51ea79d Update changelog 9600fee02 Include <filesystem> only if FMT_CPP_LIB_FILESYSTEM is set (#4258) 47a66c5ec Bump msys2/setup-msys2 from 2.24.0 to 2.25.0 (#4250) 385c01dc7 Allow bit_cast to work for 80bit long double (#4246) df249d8ad Remove an old workaround dfad80d1c Remove an old workaround 536cabd56 Export all range join overloads (#4239) b1a054706 Remove more MSVC 2015 workarounds and fix string_view checks bfd95392c Remove MSVC 2015 workaround 9ced61bca Replace std::forward for clang-tidy (#4236) 75e5be6ad Sort specifiers a169d7fa4 Fix chrono formatting syntax doc (#4235) a6c45dfea Fix modular build a35389b3c Corrently handle buffer flush 5a3576acc Implement fmt::join for tuple-like objects (#4230) 542600013 Suppress MSVC warnings "C4127: conditional expression is constant" by used const_check (#4233) 720da57ba Remove reference to unused intrinsic 680db66c3 Explicitly export symbols from detail 56ce41ef6 Remove initializer_list dependency cf50e4d6a Fix const[expr] in context API 6580d7b80 Cleanup the format API 7e73566ce Minor cleanup 8523dba2d Make constexpr precede explicit consistently e3d3b24fc Minor cleanup 1521bba70 Use consistent types for argument count 00649552a Bump github/codeql-action from 3.26.6 to 3.27.0 (#4223) 4b8e2838f More cleanup 7d4662f7a Remove FMT_BUILTIN_CTZ 27110bc47 Minor cleanup 68f315376 Fix narrowing conversion warning in struct fstring (#4210) 168df9a06 Implement fmt::format_to into std::vector<char> (#4211) 4daa3d591 Fix error: cannot use 'try' with exceptions disabled in Win LLVM Clang (#4208) e9eaa27e5 Add std::exception to the docs 2b6a786e3 Use standard context in print a16ff5787 Add support for code units > 0xFFFF in fill 601be1cbe Add support for code units > 0xFFFF in fill 58c185b63 Changing type of data_ to size_t to avoid compilation warnings (#4200) a0a9ba2af Fix hashes cc2ba8f9e Cleanup cifuzz action a18d42b20 Simplify lint (#4197) 4046f9727 Fix -Wmissing-noreturn warning (#4194) 6bdc12a19 detail_exported -> detail 786a4b096 Cleanup fixed_string 2cb3b7c64 Update README.md e9cba6905 Update README.md 02537548f Cleanup an example c68c5fa7c Test FMT_BUILTIN_TYPES 22701d5f6 Address build failures when using Tip-of-Tree clang. (#4187) e62c41ffb Conform `std::iterator_traits<fmt::appender>` to [iterator.traits]/1 (#4185) 18792893d Silencing Wextra-semi warning (#4188) c90bc9186 Bump actions/checkout from 4.1.6 to 4.2.0 (#4182) c95722ad6 Improve naming consistency db06b0df8 Use countl_zero in bigint b9ec48d9c Cleanup bigint 3faf6f181 Add min_of/max_of d64b100a3 Relax constexpr ff9ee0461 Fix handling FMT_BUILTIN_TYPES 1c5883bef Test nondeterministic conversion to format string cacc3108c Don't assume repeated evaluation of string literal produce the same pointer fade652ad Require clang >=15 for _BitInt support (#4176) 96dca569a Module linkage fixes for shared build (#4169) 891c9a73a Cleanup format API 9282222b7 Export more e5b20ff0d Deprecate detail::locale_ref ff9222354 Simplify locale handling 80c4d42c6 Cleanup format.h git-subtree-dir: external/fmt git-subtree-split: 093b39ca5eea129b111060839602bcfaf295125a
This commit is contained in:
6
.github/workflows/cifuzz.yml
vendored
6
.github/workflows/cifuzz.yml
vendored
@@ -10,18 +10,20 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Build fuzzers
|
- name: Build fuzzers
|
||||||
id: build
|
id: build
|
||||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@92182553173581f871130c71c71b17f003d47b0a # master
|
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@92182553173581f871130c71c71b17f003d47b0a
|
||||||
with:
|
with:
|
||||||
oss-fuzz-project-name: 'fmt'
|
oss-fuzz-project-name: 'fmt'
|
||||||
dry-run: false
|
dry-run: false
|
||||||
language: c++
|
language: c++
|
||||||
|
|
||||||
- name: Run fuzzers
|
- name: Run fuzzers
|
||||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@92182553173581f871130c71c71b17f003d47b0a # master
|
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@92182553173581f871130c71c71b17f003d47b0a
|
||||||
with:
|
with:
|
||||||
oss-fuzz-project-name: 'fmt'
|
oss-fuzz-project-name: 'fmt'
|
||||||
fuzz-seconds: 300
|
fuzz-seconds: 300
|
||||||
dry-run: false
|
dry-run: false
|
||||||
language: c++
|
language: c++
|
||||||
|
|
||||||
- name: Upload crash
|
- name: Upload crash
|
||||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||||
if: failure() && steps.build.outcome == 'success'
|
if: failure() && steps.build.outcome == 'success'
|
||||||
|
|||||||
2
.github/workflows/doc.yml
vendored
2
.github/workflows/doc.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
|||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
||||||
|
|
||||||
- name: Add Ubuntu mirrors
|
- name: Add Ubuntu mirrors
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
12
.github/workflows/lint.yml
vendored
12
.github/workflows/lint.yml
vendored
@@ -13,14 +13,16 @@ jobs:
|
|||||||
format_code:
|
format_code:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
||||||
|
|
||||||
- name: Install clang-format
|
- name: Install clang-format
|
||||||
uses: aminya/setup-cpp@290824452986e378826155f3379d31bce8753d76 # v0.37.0
|
run: |
|
||||||
with:
|
wget https://apt.llvm.org/llvm.sh
|
||||||
clangformat: 17.0.5
|
sudo bash ./llvm.sh 17
|
||||||
|
sudo apt install clang-format-17
|
||||||
|
|
||||||
- name: Run clang-format
|
- name: Run clang-format
|
||||||
run: |
|
run: |
|
||||||
find include src -name '*.h' -o -name '*.cc' | xargs clang-format -i -style=file -fallback-style=none
|
find include src -name '*.h' -o -name '*.cc' | \
|
||||||
|
xargs clang-format-17 -i -style=file -fallback-style=none
|
||||||
git diff --exit-code
|
git diff --exit-code
|
||||||
|
|||||||
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@@ -62,7 +62,7 @@ jobs:
|
|||||||
shared: -DBUILD_SHARED_LIBS=ON
|
shared: -DBUILD_SHARED_LIBS=ON
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
||||||
|
|
||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
run: sudo timedatectl set-timezone 'Asia/Yekaterinburg'
|
run: sudo timedatectl set-timezone 'Asia/Yekaterinburg'
|
||||||
|
|||||||
2
.github/workflows/macos.yml
vendored
2
.github/workflows/macos.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
runs-on: '${{ matrix.os }}'
|
runs-on: '${{ matrix.os }}'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
||||||
|
|
||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
run: sudo systemsetup -settimezone 'Asia/Yekaterinburg'
|
run: sudo systemsetup -settimezone 'Asia/Yekaterinburg'
|
||||||
|
|||||||
4
.github/workflows/scorecard.yml
vendored
4
.github/workflows/scorecard.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
@@ -60,6 +60,6 @@ jobs:
|
|||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|||||||
6
.github/workflows/windows.yml
vendored
6
.github/workflows/windows.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
|||||||
standard: 20
|
standard: 20
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
||||||
|
|
||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
run: tzutil /s "Ekaterinburg Standard Time"
|
run: tzutil /s "Ekaterinburg Standard Time"
|
||||||
@@ -78,12 +78,12 @@ jobs:
|
|||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
run: tzutil /s "Ekaterinburg Standard Time"
|
run: tzutil /s "Ekaterinburg Standard Time"
|
||||||
shell: cmd
|
shell: cmd
|
||||||
- uses: msys2/setup-msys2@5df0ca6cbf14efcd08f8d5bd5e049a3cc8e07fd2 # v2.24.0
|
- uses: msys2/setup-msys2@c52d1fa9c7492275e60fe763540fb601f5f232a1 # v2.25.0
|
||||||
with:
|
with:
|
||||||
release: false
|
release: false
|
||||||
msystem: ${{matrix.sys}}
|
msystem: ${{matrix.sys}}
|
||||||
pacboy: cc:p cmake:p ninja:p lld:p
|
pacboy: cc:p cmake:p ninja:p lld:p
|
||||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug
|
run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug
|
||||||
env: { LDFLAGS: -fuse-ld=lld }
|
env: { LDFLAGS: -fuse-ld=lld }
|
||||||
|
|||||||
@@ -426,7 +426,9 @@ if (FMT_INSTALL)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Install the library and headers.
|
# Install the library and headers.
|
||||||
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
|
install(TARGETS ${INSTALL_TARGETS}
|
||||||
|
COMPONENT core
|
||||||
|
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"
|
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
|
||||||
@@ -439,13 +441,15 @@ if (FMT_INSTALL)
|
|||||||
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
|
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
|
||||||
|
|
||||||
# Install version, config and target files.
|
# Install version, config and target files.
|
||||||
install(
|
install(FILES ${project_config} ${version_config}
|
||||||
FILES ${project_config} ${version_config}
|
DESTINATION ${FMT_CMAKE_DIR}
|
||||||
DESTINATION ${FMT_CMAKE_DIR})
|
COMPONENT core)
|
||||||
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
||||||
NAMESPACE fmt::)
|
NAMESPACE fmt::
|
||||||
|
COMPONENT core)
|
||||||
|
|
||||||
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
|
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}"
|
||||||
|
COMPONENT core)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
function(add_doc_target)
|
function(add_doc_target)
|
||||||
@@ -481,7 +485,8 @@ function(add_doc_target)
|
|||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-html/
|
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-html/
|
||||||
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt OPTIONAL)
|
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt
|
||||||
|
COMPONENT doc OPTIONAL)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
if (FMT_DOC)
|
if (FMT_DOC)
|
||||||
|
|||||||
238
ChangeLog.md
238
ChangeLog.md
@@ -1,6 +1,237 @@
|
|||||||
# 11.1.0 - TBD
|
# 11.1.1 - 2024-12-27
|
||||||
|
|
||||||
- Improved debug codegen.
|
- Fixed ABI compatibility with earlier 11.x versions
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4278).
|
||||||
|
|
||||||
|
- Defined CMake components (`core` and `doc`) to allow docs to be installed
|
||||||
|
separately (https://github.com/fmtlib/fmt/pull/4276).
|
||||||
|
Thanks @carlsmedstad.
|
||||||
|
|
||||||
|
# 11.1.0 - 2024-12-25
|
||||||
|
|
||||||
|
- Improved C++20 module support
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4081,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4083,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4084,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4152,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4153,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4169,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4190,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4234,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4239).
|
||||||
|
Thanks @kamrann and @Arghnews.
|
||||||
|
|
||||||
|
- Reduced debug (unoptimized) binary code size and the number of template
|
||||||
|
instantiations when passing formatting arguments. For example, unoptimized
|
||||||
|
binary code size for `fmt::print("{}", 42)` was reduced by ~40% on GCC and
|
||||||
|
~60% on clang (x86-64).
|
||||||
|
|
||||||
|
GCC:
|
||||||
|
- Before: 161 instructions of which 105 are in reusable functions
|
||||||
|
([godbolt](https://www.godbolt.org/z/s9bGoo4ze)).
|
||||||
|
- After: 116 instructions of which 60 are in reusable functions
|
||||||
|
([godbolt](https://www.godbolt.org/z/r7GGGxMs6)).
|
||||||
|
|
||||||
|
Clang:
|
||||||
|
- Before: 310 instructions of which 251 are in reusable functions
|
||||||
|
([godbolt](https://www.godbolt.org/z/Ts88b7M9o)).
|
||||||
|
- After: 194 instructions of which 135 are in reusable functions
|
||||||
|
([godbolt](https://www.godbolt.org/z/vcrjP8ceW)).
|
||||||
|
|
||||||
|
- Added an experimental `fmt::writer` API that can be used for writing to
|
||||||
|
different destinations such as files or strings
|
||||||
|
(https://github.com/fmtlib/fmt/issues/2354).
|
||||||
|
For example ([godbolt](https://www.godbolt.org/z/rWoKfbP7e)):
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <fmt/os.h>
|
||||||
|
|
||||||
|
void write_text(fmt::writer w) {
|
||||||
|
w.print("The answer is {}.", 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// Write to FILE.
|
||||||
|
write_text(stdout);
|
||||||
|
|
||||||
|
// Write to fmt::ostream.
|
||||||
|
auto f = fmt::output_file("myfile");
|
||||||
|
write_text(f);
|
||||||
|
|
||||||
|
// Write to std::string.
|
||||||
|
auto sb = fmt::string_buffer();
|
||||||
|
write_text(sb);
|
||||||
|
std::string s = sb.str();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- Added width and alignment support to the formatter of `std::error_code`.
|
||||||
|
|
||||||
|
- Made `std::expected<void, E>` formattable
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4145,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4148).
|
||||||
|
For example ([godbolt](https://www.godbolt.org/z/hrj5c6G86)):
|
||||||
|
|
||||||
|
```c++
|
||||||
|
fmt::print("{}", std::expected<void, int>());
|
||||||
|
```
|
||||||
|
|
||||||
|
prints
|
||||||
|
|
||||||
|
```
|
||||||
|
expected()
|
||||||
|
```
|
||||||
|
|
||||||
|
Thanks @phprus.
|
||||||
|
|
||||||
|
- Made `fmt::is_formattable<void>` SFINAE-friendly
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4147).
|
||||||
|
|
||||||
|
- Added support for `_BitInt` formatting when using clang
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4007,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4072,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4140,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4173,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4176).
|
||||||
|
For example ([godbolt](https://www.godbolt.org/z/KWjbWec5z)):
|
||||||
|
|
||||||
|
```c++
|
||||||
|
using int42 = _BitInt(42);
|
||||||
|
fmt::print("{}", int42(100));
|
||||||
|
```
|
||||||
|
|
||||||
|
Thanks @Arghnews.
|
||||||
|
|
||||||
|
- Added the `n` specifier for tuples and pairs
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4107). Thanks @someonewithpc.
|
||||||
|
|
||||||
|
- Added support for tuple-like types to `fmt::join`
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4226,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4230). Thanks @phprus.
|
||||||
|
|
||||||
|
- Made more types formattable at compile time
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4127). Thanks @AnthonyVH.
|
||||||
|
|
||||||
|
- Implemented a more efficient compile-time `fmt::formatted_size`
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4102,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4103). Thanks @phprus.
|
||||||
|
|
||||||
|
- Fixed compile-time formatting of some string types
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4065). Thanks @torshepherd.
|
||||||
|
|
||||||
|
- Made compiled version of `fmt::format_to` work with
|
||||||
|
`std::back_insert_iterator<std::vector<char>>`
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4206,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4211). Thanks @phprus.
|
||||||
|
|
||||||
|
- Added a formatter for `std::reference_wrapper`
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4163,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4164). Thanks @yfeldblum and @phprus.
|
||||||
|
|
||||||
|
- Added experimental padding support (glibc `strftime` extension) to `%m`, `%j`
|
||||||
|
and `%Y` (https://github.com/fmtlib/fmt/pull/4161). Thanks @KKhanhH.
|
||||||
|
|
||||||
|
- Made microseconds formatted as `us` instead of `µs` if the Unicode support is
|
||||||
|
disabled (https://github.com/fmtlib/fmt/issues/4088).
|
||||||
|
|
||||||
|
- Fixed an unreleased regression in transcoding of surrogate pairs
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4094,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4095). Thanks @phprus.
|
||||||
|
|
||||||
|
- Made `fmt::appender` satisfy `std::output_iterator` concept
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4092,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4093). Thanks @phprus.
|
||||||
|
|
||||||
|
- Made `std::iterator_traits<fmt::appender>` standard-conforming
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4185). Thanks @CaseyCarter.
|
||||||
|
|
||||||
|
- Made it easier to reuse `fmt::formatter<std::string_view>` for types with
|
||||||
|
an implicit conversion to `std::string_view`
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4036,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4055). Thanks @Arghnews.
|
||||||
|
|
||||||
|
- Made it possible to disable `<filesystem>` use via `FMT_CPP_LIB_FILESYSTEM`
|
||||||
|
for compatibility with some video game console SDKs, e.g. Nintendo Switch SDK
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4257,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4258,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4259). Thanks @W4RH4WK and @phprus.
|
||||||
|
|
||||||
|
- Fixed compatibility with platforms that use 80-bit `long double`
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4245,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4246). Thanks @jsirpoma.
|
||||||
|
|
||||||
|
- Added support for UTF-32 code units greater than `0xFFFF` in fill
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4201).
|
||||||
|
|
||||||
|
- Fixed handling of legacy encodings on Windows with GCC
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4162).
|
||||||
|
|
||||||
|
- Made `fmt::to_string` take `fmt::basic_memory_buffer` by const reference
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4261,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4262). Thanks @sascha-devel.
|
||||||
|
|
||||||
|
- Added `fmt::dynamic_format_arg_store::size`
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4270). Thanks @hannes-harnisch.
|
||||||
|
|
||||||
|
- Removed the ability to control locale usage via an undocumented
|
||||||
|
`FMT_STATIC_THOUSANDS_SEPARATOR` in favor of `FMT_USE_LOCALE`.
|
||||||
|
|
||||||
|
- Renamed `FMT_EXCEPTIONS` to `FMT_USE_EXCEPTIONS` for consistency with other
|
||||||
|
similar macros.
|
||||||
|
|
||||||
|
- Improved include directory ordering to reduce the chance of including
|
||||||
|
incorrect headers when using multiple versions of {fmt}
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4116). Thanks @cdzhan.
|
||||||
|
|
||||||
|
- Made it possible to compile a subset of {fmt} without the C++ runtime.
|
||||||
|
|
||||||
|
- Improved documentation and README
|
||||||
|
(https://github.com/fmtlib/fmt/pull/4066,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4117,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4203,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4235). Thanks @zyctree and @nikola-sh.
|
||||||
|
|
||||||
|
- Improved the documentation generator (https://github.com/fmtlib/fmt/pull/4110,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4115). Thanks @rturrado.
|
||||||
|
|
||||||
|
- Improved CI (https://github.com/fmtlib/fmt/pull/4155,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4151). Thanks @phprus.
|
||||||
|
|
||||||
|
- Fixed various warnings and compilation issues
|
||||||
|
(https://github.com/fmtlib/fmt/issues/2708,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4091,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4109,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4113,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4125,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4129,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4130,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4131,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4132,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4133,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4144,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4150,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4158,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4159,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4160,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4170,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4177,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4187,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4188,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4194,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4200,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4205,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4207,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4208,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4210,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4220,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4231,
|
||||||
|
https://github.com/fmtlib/fmt/issues/4232,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4233,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4236,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4267,
|
||||||
|
https://github.com/fmtlib/fmt/pull/4271).
|
||||||
|
Thanks @torsten48, @Arghnews, @tinfoilboy, @aminya, @Ottani, @zeroomega,
|
||||||
|
@c4v4, @kongy, @vinayyadav3016, @sergio-nsk, @phprus and @YexuanXiao.
|
||||||
|
|
||||||
# 11.0.2 - 2024-07-20
|
# 11.0.2 - 2024-07-20
|
||||||
|
|
||||||
@@ -272,6 +503,9 @@
|
|||||||
- Fixed handling of negative ids in `fmt::basic_format_args::get`
|
- Fixed handling of negative ids in `fmt::basic_format_args::get`
|
||||||
(https://github.com/fmtlib/fmt/pull/3945). Thanks @marlenecota.
|
(https://github.com/fmtlib/fmt/pull/3945). Thanks @marlenecota.
|
||||||
|
|
||||||
|
- Fixed handling of a buffer boundary on flush
|
||||||
|
(https://github.com/fmtlib/fmt/issues/4229).
|
||||||
|
|
||||||
- Improved named argument validation
|
- Improved named argument validation
|
||||||
(https://github.com/fmtlib/fmt/issues/3817).
|
(https://github.com/fmtlib/fmt/issues/3817).
|
||||||
|
|
||||||
|
|||||||
@@ -291,6 +291,7 @@ converts to `std::print`.)
|
|||||||
- [ccache](https://ccache.dev/): a compiler cache
|
- [ccache](https://ccache.dev/): a compiler cache
|
||||||
- [ClickHouse](https://github.com/ClickHouse/ClickHouse): an
|
- [ClickHouse](https://github.com/ClickHouse/ClickHouse): an
|
||||||
analytical database management system
|
analytical database management system
|
||||||
|
- [ContextVision](https://www.contextvision.com/): medical imaging software
|
||||||
- [Contour](https://github.com/contour-terminal/contour/): a modern
|
- [Contour](https://github.com/contour-terminal/contour/): a modern
|
||||||
terminal emulator
|
terminal emulator
|
||||||
- [CUAUV](https://cuauv.org/): Cornell University\'s autonomous
|
- [CUAUV](https://cuauv.org/): Cornell University\'s autonomous
|
||||||
|
|||||||
75
doc/api.md
75
doc/api.md
@@ -269,18 +269,16 @@ that support C++20 `consteval`. On older compilers you can use the
|
|||||||
|
|
||||||
Unused arguments are allowed as in Python's `str.format` and ordinary functions.
|
Unused arguments are allowed as in Python's `str.format` and ordinary functions.
|
||||||
|
|
||||||
::: basic_format_string
|
See [Type Erasure](#type-erasure) for an example of how to enable compile-time
|
||||||
|
checks in your own functions with `fmt::format_string` while avoiding template
|
||||||
|
bloat.
|
||||||
|
|
||||||
|
::: fstring
|
||||||
|
|
||||||
::: format_string
|
::: format_string
|
||||||
|
|
||||||
::: runtime(string_view)
|
::: runtime(string_view)
|
||||||
|
|
||||||
### Named Arguments
|
|
||||||
|
|
||||||
::: arg(const Char*, const T&)
|
|
||||||
|
|
||||||
Named arguments are not supported in compile-time checks at the moment.
|
|
||||||
|
|
||||||
### Type Erasure
|
### Type Erasure
|
||||||
|
|
||||||
You can create your own formatting function with compile-time checks and
|
You can create your own formatting function with compile-time checks and
|
||||||
@@ -317,6 +315,12 @@ parameterized version.
|
|||||||
|
|
||||||
::: basic_format_arg
|
::: basic_format_arg
|
||||||
|
|
||||||
|
### Named Arguments
|
||||||
|
|
||||||
|
::: arg(const Char*, const T&)
|
||||||
|
|
||||||
|
Named arguments are not supported in compile-time checks at the moment.
|
||||||
|
|
||||||
### Compatibility
|
### Compatibility
|
||||||
|
|
||||||
::: basic_string_view
|
::: basic_string_view
|
||||||
@@ -375,18 +379,17 @@ allocator:
|
|||||||
using custom_string =
|
using custom_string =
|
||||||
std::basic_string<char, std::char_traits<char>, custom_allocator>;
|
std::basic_string<char, std::char_traits<char>, custom_allocator>;
|
||||||
|
|
||||||
custom_string vformat(custom_allocator alloc, fmt::string_view format_str,
|
auto vformat(custom_allocator alloc, fmt::string_view fmt,
|
||||||
fmt::format_args args) {
|
fmt::format_args args) -> custom_string {
|
||||||
auto buf = custom_memory_buffer(alloc);
|
auto buf = custom_memory_buffer(alloc);
|
||||||
fmt::vformat_to(std::back_inserter(buf), format_str, args);
|
fmt::vformat_to(std::back_inserter(buf), fmt, args);
|
||||||
return custom_string(buf.data(), buf.size(), alloc);
|
return custom_string(buf.data(), buf.size(), alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ...Args>
|
template <typename ...Args>
|
||||||
inline custom_string format(custom_allocator alloc,
|
auto format(custom_allocator alloc, fmt::string_view fmt,
|
||||||
fmt::string_view format_str,
|
const Args& ... args) -> custom_string {
|
||||||
const Args& ... args) {
|
return vformat(alloc, fmt, fmt::make_format_args(args...));
|
||||||
return vformat(alloc, format_str, fmt::make_format_args(args...));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
The allocator will be used for the output container only. Formatting
|
The allocator will be used for the output container only. Formatting
|
||||||
@@ -410,11 +413,11 @@ locale:
|
|||||||
that take `std::locale` as a parameter. The locale type is a template
|
that take `std::locale` as a parameter. The locale type is a template
|
||||||
parameter to avoid the expensive `<locale>` include.
|
parameter to avoid the expensive `<locale>` include.
|
||||||
|
|
||||||
::: format(const Locale&, format_string<T...>, T&&...)
|
::: format(detail::locale_ref, format_string<T...>, T&&...)
|
||||||
|
|
||||||
::: format_to(OutputIt, const Locale&, format_string<T...>, T&&...)
|
::: format_to(OutputIt, detail::locale_ref, format_string<T...>, T&&...)
|
||||||
|
|
||||||
::: formatted_size(const Locale&, format_string<T...>, T&&...)
|
::: formatted_size(detail::locale_ref, format_string<T...>, T&&...)
|
||||||
|
|
||||||
<a id="legacy-checks"></a>
|
<a id="legacy-checks"></a>
|
||||||
### Legacy Compile-Time Checks
|
### Legacy Compile-Time Checks
|
||||||
@@ -498,10 +501,13 @@ chrono-format-specifications).
|
|||||||
- [`std::atomic_flag`](https://en.cppreference.com/w/cpp/atomic/atomic_flag)
|
- [`std::atomic_flag`](https://en.cppreference.com/w/cpp/atomic/atomic_flag)
|
||||||
- [`std::bitset`](https://en.cppreference.com/w/cpp/utility/bitset)
|
- [`std::bitset`](https://en.cppreference.com/w/cpp/utility/bitset)
|
||||||
- [`std::error_code`](https://en.cppreference.com/w/cpp/error/error_code)
|
- [`std::error_code`](https://en.cppreference.com/w/cpp/error/error_code)
|
||||||
|
- [`std::exception`](https://en.cppreference.com/w/cpp/error/exception)
|
||||||
- [`std::filesystem::path`](https://en.cppreference.com/w/cpp/filesystem/path)
|
- [`std::filesystem::path`](https://en.cppreference.com/w/cpp/filesystem/path)
|
||||||
- [`std::monostate`](https://en.cppreference.com/w/cpp/utility/variant/monostate)
|
- [`std::monostate`](
|
||||||
|
https://en.cppreference.com/w/cpp/utility/variant/monostate)
|
||||||
- [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional)
|
- [`std::optional`](https://en.cppreference.com/w/cpp/utility/optional)
|
||||||
- [`std::source_location`](https://en.cppreference.com/w/cpp/utility/source_location)
|
- [`std::source_location`](
|
||||||
|
https://en.cppreference.com/w/cpp/utility/source_location)
|
||||||
- [`std::thread::id`](https://en.cppreference.com/w/cpp/thread/thread/id)
|
- [`std::thread::id`](https://en.cppreference.com/w/cpp/thread/thread/id)
|
||||||
- [`std::variant`](https://en.cppreference.com/w/cpp/utility/variant/variant)
|
- [`std::variant`](https://en.cppreference.com/w/cpp/utility/variant/variant)
|
||||||
|
|
||||||
@@ -509,7 +515,7 @@ chrono-format-specifications).
|
|||||||
|
|
||||||
::: ptr(const std::shared_ptr<T>&)
|
::: ptr(const std::shared_ptr<T>&)
|
||||||
|
|
||||||
### Formatting Variants
|
### Variants
|
||||||
|
|
||||||
A `std::variant` is only formattable if every variant alternative is
|
A `std::variant` is only formattable if every variant alternative is
|
||||||
formattable, and requires the `__cpp_lib_variant` [library
|
formattable, and requires the `__cpp_lib_variant` [library
|
||||||
@@ -525,15 +531,32 @@ feature](https://en.cppreference.com/w/cpp/feature_test).
|
|||||||
fmt::print("{}", std::variant<std::monostate, char>());
|
fmt::print("{}", std::variant<std::monostate, char>());
|
||||||
// Output: variant(monostate)
|
// Output: variant(monostate)
|
||||||
|
|
||||||
|
## Bit-Fields and Packed Structs
|
||||||
|
|
||||||
|
To format a bit-field or a field of a struct with `__attribute__((packed))`
|
||||||
|
applied to it, you need to convert it to the underlying or compatible type via
|
||||||
|
a cast or a unary `+` ([godbolt](https://www.godbolt.org/z/3qKKs6T5Y)):
|
||||||
|
|
||||||
|
```c++
|
||||||
|
struct smol {
|
||||||
|
int bit : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto s = smol();
|
||||||
|
fmt::print("{}", +s.bit);
|
||||||
|
```
|
||||||
|
|
||||||
|
This is a known limitation of "perfect" forwarding in C++.
|
||||||
|
|
||||||
<a id="compile-api"></a>
|
<a id="compile-api"></a>
|
||||||
## Format String Compilation
|
## Format String Compilation
|
||||||
|
|
||||||
`fmt/compile.h` provides format string compilation enabled via the
|
`fmt/compile.h` provides format string compilation and compile-time
|
||||||
`FMT_COMPILE` macro or the `_cf` user-defined literal defined in
|
(`constexpr`) formatting enabled via the `FMT_COMPILE` macro or the `_cf`
|
||||||
namespace `fmt::literals`. Format strings marked with `FMT_COMPILE`
|
user-defined literal defined in namespace `fmt::literals`. Format strings
|
||||||
or `_cf` are parsed, checked and converted into efficient formatting
|
marked with `FMT_COMPILE` or `_cf` are parsed, checked and converted into
|
||||||
code at compile-time. This supports arguments of built-in and string
|
efficient formatting code at compile-time. This supports arguments of built-in
|
||||||
types as well as user-defined types with `format` functions taking
|
and string types as well as user-defined types with `format` functions taking
|
||||||
the format context type as a template parameter in their `formatter`
|
the format context type as a template parameter in their `formatter`
|
||||||
specializations. For example:
|
specializations. For example:
|
||||||
|
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ For a static build, use the following subproject definition:
|
|||||||
|
|
||||||
For the header-only version, use:
|
For the header-only version, use:
|
||||||
|
|
||||||
fmt = subproject('fmt')
|
fmt = subproject('fmt', default_options: ['header-only=true'])
|
||||||
fmt_dep = fmt.get_variable('fmt_header_only_dep')
|
fmt_dep = fmt.get_variable('fmt_header_only_dep')
|
||||||
|
|
||||||
### Android NDK
|
### Android NDK
|
||||||
|
|||||||
@@ -122,8 +122,8 @@ hide:
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The library is highly portable and requires only a minimal <b>subset of
|
The library is highly portable and requires only a minimal <b>subset of
|
||||||
C++11</b> features which are available in GCC 4.9, Clang 3.4, MSVC 19.0
|
C++11</b> features which are available in GCC 4.9, Clang 3.4, MSVC 19.10
|
||||||
(2015) and later. Newer compiler and standard library features are used
|
(2017) and later. Newer compiler and standard library features are used
|
||||||
if available, and enable additional functionality.
|
if available, and enable additional functionality.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -706,12 +706,12 @@ The available padding modifiers (*padding_modifier*) are:
|
|||||||
|
|
||||||
| Type | Meaning |
|
| Type | Meaning |
|
||||||
|-------|-----------------------------------------|
|
|-------|-----------------------------------------|
|
||||||
| `'-'` | Pad a numeric result with spaces. |
|
| `'_'` | Pad a numeric result with spaces. |
|
||||||
| `'_'` | Do not pad a numeric result string. |
|
| `'-'` | Do not pad a numeric result string. |
|
||||||
| `'0'` | Pad a numeric result string with zeros. |
|
| `'0'` | Pad a numeric result string with zeros. |
|
||||||
|
|
||||||
These modifiers are only supported for the `'H'`, `'I'`, `'M'`, `'S'`, `'U'`,
|
These modifiers are only supported for the `'H'`, `'I'`, `'M'`, `'S'`, `'U'`,
|
||||||
`'V'`, `'W'`, `'m'`, `'j'`, `'Y'` presentation types.
|
`'V'`, `'W'`, `'Y'`, `'d'`, `'j'` and `'m'` presentation types.
|
||||||
|
|
||||||
## Range Format Specifications
|
## Range Format Specifications
|
||||||
|
|
||||||
|
|||||||
@@ -210,6 +210,9 @@ template <typename Context> class dynamic_format_arg_store {
|
|||||||
data_.reserve(new_cap);
|
data_.reserve(new_cap);
|
||||||
named_info_.reserve(new_cap_named);
|
named_info_.reserve(new_cap_named);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the number of elements in the store.
|
||||||
|
size_t size() const noexcept { return data_.size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||||
#define FMT_VERSION 110002
|
#define FMT_VERSION 110101
|
||||||
|
|
||||||
// Detect compiler versions.
|
// Detect compiler versions.
|
||||||
#if defined(__clang__) && !defined(__ibmxl__)
|
#if defined(__clang__) && !defined(__ibmxl__)
|
||||||
@@ -146,6 +146,8 @@
|
|||||||
// Use the provided definition.
|
// Use the provided definition.
|
||||||
#elif defined(__GNUC__) && !defined(__EXCEPTIONS)
|
#elif defined(__GNUC__) && !defined(__EXCEPTIONS)
|
||||||
# define FMT_USE_EXCEPTIONS 0
|
# define FMT_USE_EXCEPTIONS 0
|
||||||
|
#elif defined(__clang__) && !defined(__cpp_exceptions)
|
||||||
|
# define FMT_USE_EXCEPTIONS 0
|
||||||
#elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS
|
#elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS
|
||||||
# define FMT_USE_EXCEPTIONS 0
|
# define FMT_USE_EXCEPTIONS 0
|
||||||
#else
|
#else
|
||||||
@@ -159,6 +161,20 @@
|
|||||||
# define FMT_CATCH(x) if (false)
|
# define FMT_CATCH(x) if (false)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef FMT_NO_UNIQUE_ADDRESS
|
||||||
|
// Use the provided definition.
|
||||||
|
#elif FMT_CPLUSPLUS < 202002L
|
||||||
|
// Not supported.
|
||||||
|
#elif FMT_HAS_CPP_ATTRIBUTE(no_unique_address)
|
||||||
|
# define FMT_NO_UNIQUE_ADDRESS [[no_unique_address]]
|
||||||
|
// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485).
|
||||||
|
#elif FMT_MSC_VERSION >= 1929 && !FMT_CLANG_VERSION
|
||||||
|
# define FMT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
|
||||||
|
#endif
|
||||||
|
#ifndef FMT_NO_UNIQUE_ADDRESS
|
||||||
|
# define FMT_NO_UNIQUE_ADDRESS
|
||||||
|
#endif
|
||||||
|
|
||||||
#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
|
#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
|
||||||
# define FMT_FALLTHROUGH [[fallthrough]]
|
# define FMT_FALLTHROUGH [[fallthrough]]
|
||||||
#elif defined(__clang__)
|
#elif defined(__clang__)
|
||||||
@@ -332,6 +348,13 @@ struct monostate {
|
|||||||
# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
|
# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
template <typename T> constexpr auto min_of(T a, T b) -> T {
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
template <typename T> constexpr auto max_of(T a, T b) -> T {
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
// Suppresses "unused variable" warnings with the method described in
|
// Suppresses "unused variable" warnings with the method described in
|
||||||
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
|
// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/.
|
||||||
@@ -355,7 +378,9 @@ constexpr auto is_constant_evaluated(bool default_value = false) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Suppresses "conditional expression is constant" warnings.
|
// Suppresses "conditional expression is constant" warnings.
|
||||||
template <typename T> constexpr auto const_check(T value) -> T { return value; }
|
template <typename T> FMT_ALWAYS_INLINE constexpr auto const_check(T val) -> T {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
||||||
const char* message);
|
const char* message);
|
||||||
@@ -394,7 +419,7 @@ inline auto map(uint128_opt) -> monostate { return {}; }
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_USE_BITINT
|
#ifndef FMT_USE_BITINT
|
||||||
# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1400)
|
# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FMT_USE_BITINT
|
#if FMT_USE_BITINT
|
||||||
@@ -562,8 +587,8 @@ template <typename Char> class basic_string_view {
|
|||||||
|
|
||||||
// Lexicographically compare this string reference to other.
|
// Lexicographically compare this string reference to other.
|
||||||
FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
|
FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
|
||||||
size_t str_size = size_ < other.size_ ? size_ : other.size_;
|
int result =
|
||||||
int result = detail::compare(data_, other.data_, str_size);
|
detail::compare(data_, other.data_, min_of(size_, other.size_));
|
||||||
if (result != 0) return result;
|
if (result != 0) return result;
|
||||||
return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
|
return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
|
||||||
}
|
}
|
||||||
@@ -714,7 +739,7 @@ class basic_specs {
|
|||||||
max_fill_size = 4
|
max_fill_size = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned long data_ = 1 << fill_size_shift;
|
size_t data_ = 1 << fill_size_shift;
|
||||||
|
|
||||||
// Character (code unit) type is erased to prevent template bloat.
|
// Character (code unit) type is erased to prevent template bloat.
|
||||||
char fill_data_[max_fill_size] = {' '};
|
char fill_data_[max_fill_size] = {' '};
|
||||||
@@ -793,7 +818,8 @@ class basic_specs {
|
|||||||
template <typename Char> constexpr auto fill_unit() const -> Char {
|
template <typename Char> constexpr auto fill_unit() const -> Char {
|
||||||
using uchar = unsigned char;
|
using uchar = unsigned char;
|
||||||
return static_cast<Char>(static_cast<uchar>(fill_data_[0]) |
|
return static_cast<Char>(static_cast<uchar>(fill_data_[0]) |
|
||||||
(static_cast<uchar>(fill_data_[1]) << 8));
|
(static_cast<uchar>(fill_data_[1]) << 8) |
|
||||||
|
(static_cast<uchar>(fill_data_[2]) << 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void set_fill(char c) {
|
FMT_CONSTEXPR void set_fill(char c) {
|
||||||
@@ -809,12 +835,19 @@ class basic_specs {
|
|||||||
unsigned uchar = static_cast<detail::unsigned_char<Char>>(s[0]);
|
unsigned uchar = static_cast<detail::unsigned_char<Char>>(s[0]);
|
||||||
fill_data_[0] = static_cast<char>(uchar);
|
fill_data_[0] = static_cast<char>(uchar);
|
||||||
fill_data_[1] = static_cast<char>(uchar >> 8);
|
fill_data_[1] = static_cast<char>(uchar >> 8);
|
||||||
|
fill_data_[2] = static_cast<char>(uchar >> 16);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
FMT_ASSERT(size <= max_fill_size, "invalid fill");
|
FMT_ASSERT(size <= max_fill_size, "invalid fill");
|
||||||
for (size_t i = 0; i < size; ++i)
|
for (size_t i = 0; i < size; ++i)
|
||||||
fill_data_[i & 3] = static_cast<char>(s[i]);
|
fill_data_[i & 3] = static_cast<char>(s[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void set_fill(const basic_specs& specs) {
|
||||||
|
set_fill_size(specs.fill_size());
|
||||||
|
for (size_t i = 0; i < max_fill_size; ++i)
|
||||||
|
fill_data_[i] = specs.fill_data_[i];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format specifiers for built-in and string types.
|
// Format specifiers for built-in and string types.
|
||||||
@@ -842,7 +875,7 @@ template <typename Char = char> class parse_context {
|
|||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
using iterator = const Char*;
|
using iterator = const Char*;
|
||||||
|
|
||||||
explicit constexpr parse_context(basic_string_view<Char> fmt,
|
constexpr explicit parse_context(basic_string_view<Char> fmt,
|
||||||
int next_arg_id = 0)
|
int next_arg_id = 0)
|
||||||
: fmt_(fmt), next_arg_id_(next_arg_id) {}
|
: fmt_(fmt), next_arg_id_(next_arg_id) {}
|
||||||
|
|
||||||
@@ -1012,15 +1045,15 @@ template <typename Char, typename T> struct named_arg : view {
|
|||||||
static_assert(!is_named_arg<T>::value, "nested named arguments");
|
static_assert(!is_named_arg<T>::value, "nested named arguments");
|
||||||
};
|
};
|
||||||
|
|
||||||
template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
|
template <bool B = false> constexpr auto count() -> int { return B ? 1 : 0; }
|
||||||
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
|
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
|
||||||
return (B1 ? 1 : 0) + count<B2, Tail...>();
|
return (B1 ? 1 : 0) + count<B2, Tail...>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args> constexpr auto count_named_args() -> size_t {
|
template <typename... Args> constexpr auto count_named_args() -> int {
|
||||||
return count<is_named_arg<Args>::value...>();
|
return count<is_named_arg<Args>::value...>();
|
||||||
}
|
}
|
||||||
template <typename... Args> constexpr auto count_static_named_args() -> size_t {
|
template <typename... Args> constexpr auto count_static_named_args() -> int {
|
||||||
return count<is_static_named_arg<Args>::value...>();
|
return count<is_static_named_arg<Args>::value...>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1180,7 +1213,7 @@ class compile_parse_context : public parse_context<Char> {
|
|||||||
using base = parse_context<Char>;
|
using base = parse_context<Char>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FMT_CONSTEXPR compile_parse_context(basic_string_view<Char> fmt,
|
FMT_CONSTEXPR explicit compile_parse_context(basic_string_view<Char> fmt,
|
||||||
int num_args, const type* types,
|
int num_args, const type* types,
|
||||||
int next_arg_id = 0)
|
int next_arg_id = 0)
|
||||||
: base(fmt, next_arg_id), num_args_(num_args), types_(types) {}
|
: base(fmt, next_arg_id), num_args_(num_args), types_(types) {}
|
||||||
@@ -1627,16 +1660,16 @@ template <typename... T> struct arg_pack {};
|
|||||||
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
|
template <typename Char, int NUM_ARGS, int NUM_NAMED_ARGS, bool DYNAMIC_NAMES>
|
||||||
class format_string_checker {
|
class format_string_checker {
|
||||||
private:
|
private:
|
||||||
type types_[NUM_ARGS > 0 ? NUM_ARGS : 1];
|
type types_[max_of(1, NUM_ARGS)];
|
||||||
named_arg_info<Char> named_args_[NUM_NAMED_ARGS > 0 ? NUM_NAMED_ARGS : 1];
|
named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
|
||||||
compile_parse_context<Char> context_;
|
compile_parse_context<Char> context_;
|
||||||
|
|
||||||
using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
|
using parse_func = auto (*)(parse_context<Char>&) -> const Char*;
|
||||||
parse_func parse_funcs_[NUM_ARGS > 0 ? NUM_ARGS : 1];
|
parse_func parse_funcs_[max_of(1, NUM_ARGS)];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
explicit FMT_CONSTEXPR format_string_checker(basic_string_view<Char> fmt,
|
FMT_CONSTEXPR explicit format_string_checker(basic_string_view<Char> fmt,
|
||||||
arg_pack<T...>)
|
arg_pack<T...>)
|
||||||
: types_{mapped_type_constant<T, Char>::value...},
|
: types_{mapped_type_constant<T, Char>::value...},
|
||||||
named_args_{},
|
named_args_{},
|
||||||
@@ -1694,7 +1727,7 @@ template <typename T> class buffer {
|
|||||||
protected:
|
protected:
|
||||||
// Don't initialize ptr_ since it is not accessed to save a few cycles.
|
// Don't initialize ptr_ since it is not accessed to save a few cycles.
|
||||||
FMT_MSC_WARNING(suppress : 26495)
|
FMT_MSC_WARNING(suppress : 26495)
|
||||||
FMT_CONSTEXPR20 buffer(grow_fun grow, size_t sz) noexcept
|
FMT_CONSTEXPR buffer(grow_fun grow, size_t sz) noexcept
|
||||||
: size_(sz), capacity_(sz), grow_(grow) {}
|
: size_(sz), capacity_(sz), grow_(grow) {}
|
||||||
|
|
||||||
constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0,
|
constexpr buffer(grow_fun grow, T* p = nullptr, size_t sz = 0,
|
||||||
@@ -1740,7 +1773,7 @@ template <typename T> class buffer {
|
|||||||
// the new elements may not be initialized.
|
// the new elements may not be initialized.
|
||||||
FMT_CONSTEXPR void try_resize(size_t count) {
|
FMT_CONSTEXPR void try_resize(size_t count) {
|
||||||
try_reserve(count);
|
try_reserve(count);
|
||||||
size_ = count <= capacity_ ? count : capacity_;
|
size_ = min_of(count, capacity_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tries increasing the buffer capacity to `new_capacity`. It can increase the
|
// Tries increasing the buffer capacity to `new_capacity`. It can increase the
|
||||||
@@ -1788,9 +1821,9 @@ template <typename T> class buffer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct buffer_traits {
|
struct buffer_traits {
|
||||||
explicit buffer_traits(size_t) {}
|
constexpr explicit buffer_traits(size_t) {}
|
||||||
auto count() const -> size_t { return 0; }
|
constexpr auto count() const -> size_t { return 0; }
|
||||||
auto limit(size_t size) -> size_t { return size; }
|
constexpr auto limit(size_t size) const -> size_t { return size; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class fixed_buffer_traits {
|
class fixed_buffer_traits {
|
||||||
@@ -1799,12 +1832,12 @@ class fixed_buffer_traits {
|
|||||||
size_t limit_;
|
size_t limit_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
|
constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
|
||||||
auto count() const -> size_t { return count_; }
|
constexpr auto count() const -> size_t { return count_; }
|
||||||
auto limit(size_t size) -> size_t {
|
FMT_CONSTEXPR auto limit(size_t size) -> size_t {
|
||||||
size_t n = limit_ > count_ ? limit_ - count_ : 0;
|
size_t n = limit_ > count_ ? limit_ - count_ : 0;
|
||||||
count_ += size;
|
count_ += size;
|
||||||
return size < n ? size : n;
|
return min_of(size, n);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1962,15 +1995,37 @@ template <typename T = char> class counting_buffer : public buffer<T> {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
struct is_back_insert_iterator<basic_appender<T>> : std::true_type {};
|
struct is_back_insert_iterator<basic_appender<T>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename OutputIt, typename InputIt, typename = void>
|
||||||
|
struct has_back_insert_iterator_container_append : std::false_type {};
|
||||||
|
template <typename OutputIt, typename InputIt>
|
||||||
|
struct has_back_insert_iterator_container_append<
|
||||||
|
OutputIt, InputIt,
|
||||||
|
void_t<decltype(get_container(std::declval<OutputIt>())
|
||||||
|
.append(std::declval<InputIt>(),
|
||||||
|
std::declval<InputIt>()))>> : std::true_type {};
|
||||||
|
|
||||||
// An optimized version of std::copy with the output value type (T).
|
// An optimized version of std::copy with the output value type (T).
|
||||||
template <typename T, typename InputIt, typename OutputIt,
|
template <typename T, typename InputIt, typename OutputIt,
|
||||||
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value)>
|
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value&&
|
||||||
|
has_back_insert_iterator_container_append<
|
||||||
|
OutputIt, InputIt>::value)>
|
||||||
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
|
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
|
||||||
-> OutputIt {
|
-> OutputIt {
|
||||||
get_container(out).append(begin, end);
|
get_container(out).append(begin, end);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename InputIt, typename OutputIt,
|
||||||
|
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value &&
|
||||||
|
!has_back_insert_iterator_container_append<
|
||||||
|
OutputIt, InputIt>::value)>
|
||||||
|
FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out)
|
||||||
|
-> OutputIt {
|
||||||
|
auto& c = get_container(out);
|
||||||
|
c.insert(c.end(), begin, end);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, typename InputIt, typename OutputIt,
|
template <typename T, typename InputIt, typename OutputIt,
|
||||||
FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
|
FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
|
||||||
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
|
FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt {
|
||||||
@@ -2146,7 +2201,8 @@ template <typename Context> class value {
|
|||||||
template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
|
template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
|
||||||
value(const T& named_arg) : value(named_arg.value) {}
|
value(const T& named_arg) : value(named_arg.value) {}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(use_formatter<T>::value)>
|
template <typename T,
|
||||||
|
FMT_ENABLE_IF(use_formatter<T>::value || !FMT_BUILTIN_TYPES)>
|
||||||
FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {}
|
FMT_CONSTEXPR20 FMT_INLINE value(T& x) : value(x, custom_tag()) {}
|
||||||
|
|
||||||
FMT_ALWAYS_INLINE value(const named_arg_info<char_type>* args, size_t size)
|
FMT_ALWAYS_INLINE value(const named_arg_info<char_type>* args, size_t size)
|
||||||
@@ -2220,9 +2276,12 @@ struct locale_ref {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
constexpr locale_ref() : locale_(nullptr) {}
|
constexpr locale_ref() : locale_(nullptr) {}
|
||||||
template <typename Locale> explicit locale_ref(const Locale& loc);
|
|
||||||
explicit operator bool() const noexcept { return locale_ != nullptr; }
|
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
|
||||||
#endif
|
locale_ref(const Locale& loc);
|
||||||
|
|
||||||
|
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
|
||||||
|
#endif // FMT_USE_LOCALE
|
||||||
|
|
||||||
template <typename Locale> auto get() const -> Locale;
|
template <typename Locale> auto get() const -> Locale;
|
||||||
};
|
};
|
||||||
@@ -2243,16 +2302,15 @@ constexpr auto make_descriptor() -> unsigned long long {
|
|||||||
: is_unpacked_bit | NUM_ARGS;
|
: is_unpacked_bit | NUM_ARGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Context, size_t NUM_ARGS>
|
template <typename Context, int NUM_ARGS>
|
||||||
using arg_t = conditional_t<NUM_ARGS <= max_packed_args, value<Context>,
|
using arg_t = conditional_t<NUM_ARGS <= max_packed_args, value<Context>,
|
||||||
basic_format_arg<Context>>;
|
basic_format_arg<Context>>;
|
||||||
|
|
||||||
template <typename Context, size_t NUM_ARGS, size_t NUM_NAMED_ARGS,
|
template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
|
||||||
unsigned long long DESC>
|
unsigned long long DESC>
|
||||||
struct named_arg_store {
|
struct named_arg_store {
|
||||||
// args_[0].named_args points to named_args to avoid bloating format_args.
|
// args_[0].named_args points to named_args to avoid bloating format_args.
|
||||||
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
|
arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
|
||||||
arg_t<Context, NUM_ARGS> args[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
|
|
||||||
named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
|
named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
@@ -2280,13 +2338,13 @@ struct named_arg_store {
|
|||||||
// An array of references to arguments. It can be implicitly converted to
|
// An array of references to arguments. It can be implicitly converted to
|
||||||
// `basic_format_args` for passing into type-erased formatting functions
|
// `basic_format_args` for passing into type-erased formatting functions
|
||||||
// such as `vformat`. It is a plain struct to reduce binary size in debug mode.
|
// such as `vformat`. It is a plain struct to reduce binary size in debug mode.
|
||||||
template <typename Context, size_t NUM_ARGS, size_t NUM_NAMED_ARGS,
|
template <typename Context, int NUM_ARGS, int NUM_NAMED_ARGS,
|
||||||
unsigned long long DESC>
|
unsigned long long DESC>
|
||||||
struct format_arg_store {
|
struct format_arg_store {
|
||||||
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
|
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
|
||||||
using type =
|
using type =
|
||||||
conditional_t<NUM_NAMED_ARGS == 0,
|
conditional_t<NUM_NAMED_ARGS == 0,
|
||||||
arg_t<Context, NUM_ARGS>[NUM_ARGS != 0 ? NUM_ARGS : +1],
|
arg_t<Context, NUM_ARGS>[max_of(1, NUM_ARGS)],
|
||||||
named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
|
named_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>>;
|
||||||
type args;
|
type args;
|
||||||
};
|
};
|
||||||
@@ -2372,11 +2430,6 @@ template <typename T> class basic_appender {
|
|||||||
detail::buffer<T>* container;
|
detail::buffer<T>* container;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using iterator_category = int;
|
|
||||||
using value_type = T;
|
|
||||||
using pointer = T*;
|
|
||||||
using reference = T&;
|
|
||||||
using difference_type = decltype(pointer() - pointer());
|
|
||||||
using container_type = detail::buffer<T>;
|
using container_type = detail::buffer<T>;
|
||||||
|
|
||||||
FMT_CONSTEXPR basic_appender(detail::buffer<T>& buf) : container(&buf) {}
|
FMT_CONSTEXPR basic_appender(detail::buffer<T>& buf) : container(&buf) {}
|
||||||
@@ -2503,7 +2556,7 @@ template <typename Context> class basic_format_args {
|
|||||||
return static_cast<detail::type>((desc_ >> shift) & mask);
|
return static_cast<detail::type>((desc_ >> shift) & mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t NUM_ARGS, size_t NUM_NAMED_ARGS, unsigned long long DESC>
|
template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC>
|
||||||
using store =
|
using store =
|
||||||
detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>;
|
detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>;
|
||||||
|
|
||||||
@@ -2513,14 +2566,14 @@ template <typename Context> class basic_format_args {
|
|||||||
constexpr basic_format_args() : desc_(0), args_(nullptr) {}
|
constexpr basic_format_args() : desc_(0), args_(nullptr) {}
|
||||||
|
|
||||||
/// Constructs a `basic_format_args` object from `format_arg_store`.
|
/// Constructs a `basic_format_args` object from `format_arg_store`.
|
||||||
template <size_t NUM_ARGS, size_t NUM_NAMED_ARGS, unsigned long long DESC,
|
template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC,
|
||||||
FMT_ENABLE_IF(NUM_ARGS <= detail::max_packed_args)>
|
FMT_ENABLE_IF(NUM_ARGS <= detail::max_packed_args)>
|
||||||
constexpr FMT_ALWAYS_INLINE basic_format_args(
|
constexpr FMT_ALWAYS_INLINE basic_format_args(
|
||||||
const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
|
const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
|
||||||
: desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
|
: desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
|
||||||
values_(s.args) {}
|
values_(s.args) {}
|
||||||
|
|
||||||
template <size_t NUM_ARGS, size_t NUM_NAMED_ARGS, unsigned long long DESC,
|
template <int NUM_ARGS, int NUM_NAMED_ARGS, unsigned long long DESC,
|
||||||
FMT_ENABLE_IF(NUM_ARGS > detail::max_packed_args)>
|
FMT_ENABLE_IF(NUM_ARGS > detail::max_packed_args)>
|
||||||
constexpr basic_format_args(const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
|
constexpr basic_format_args(const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
|
||||||
: desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
|
: desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
|
||||||
@@ -2571,10 +2624,11 @@ template <typename Context> class basic_format_args {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// A formatting context.
|
// A formatting context.
|
||||||
class context : private detail::locale_ref {
|
class context {
|
||||||
private:
|
private:
|
||||||
appender out_;
|
appender out_;
|
||||||
format_args args_;
|
format_args args_;
|
||||||
|
FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// The character type for the output.
|
/// The character type for the output.
|
||||||
@@ -2590,24 +2644,26 @@ class context : private detail::locale_ref {
|
|||||||
/// in the object so make sure they have appropriate lifetimes.
|
/// in the object so make sure they have appropriate lifetimes.
|
||||||
FMT_CONSTEXPR context(iterator out, format_args args,
|
FMT_CONSTEXPR context(iterator out, format_args args,
|
||||||
detail::locale_ref loc = {})
|
detail::locale_ref loc = {})
|
||||||
: locale_ref(loc), out_(out), args_(args) {}
|
: out_(out), args_(args), loc_(loc) {}
|
||||||
context(context&&) = default;
|
context(context&&) = default;
|
||||||
context(const context&) = delete;
|
context(const context&) = delete;
|
||||||
void operator=(const context&) = delete;
|
void operator=(const context&) = delete;
|
||||||
|
|
||||||
FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); }
|
FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); }
|
||||||
auto arg(string_view name) -> format_arg { return args_.get(name); }
|
inline auto arg(string_view name) const -> format_arg {
|
||||||
FMT_CONSTEXPR auto arg_id(string_view name) -> int {
|
return args_.get(name);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR auto arg_id(string_view name) const -> int {
|
||||||
return args_.get_id(name);
|
return args_.get_id(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns an iterator to the beginning of the output range.
|
// Returns an iterator to the beginning of the output range.
|
||||||
FMT_CONSTEXPR auto out() -> iterator { return out_; }
|
FMT_CONSTEXPR auto out() const -> iterator { return out_; }
|
||||||
|
|
||||||
// Advances the begin iterator to `it`.
|
// Advances the begin iterator to `it`.
|
||||||
void advance_to(iterator) {}
|
FMT_CONSTEXPR void advance_to(iterator) {}
|
||||||
|
|
||||||
FMT_CONSTEXPR auto locale() -> detail::locale_ref { return *this; }
|
FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char = char> struct runtime_format_string {
|
template <typename Char = char> struct runtime_format_string {
|
||||||
@@ -2624,7 +2680,8 @@ template <typename Char = char> struct runtime_format_string {
|
|||||||
*/
|
*/
|
||||||
inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
|
inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; }
|
||||||
|
|
||||||
/// A compile-time format string.
|
/// A compile-time format string. Use `format_string` in the public API to
|
||||||
|
/// prevent type deduction.
|
||||||
template <typename... T> struct fstring {
|
template <typename... T> struct fstring {
|
||||||
private:
|
private:
|
||||||
static constexpr int num_static_named_args =
|
static constexpr int num_static_named_args =
|
||||||
@@ -2657,8 +2714,9 @@ template <typename... T> struct fstring {
|
|||||||
template <typename S,
|
template <typename S,
|
||||||
FMT_ENABLE_IF(std::is_convertible<const S&, string_view>::value)>
|
FMT_ENABLE_IF(std::is_convertible<const S&, string_view>::value)>
|
||||||
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) {
|
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) {
|
||||||
|
auto sv = string_view(str);
|
||||||
if (FMT_USE_CONSTEVAL)
|
if (FMT_USE_CONSTEVAL)
|
||||||
detail::parse_format_string<char>(s, checker(s, arg_pack()));
|
detail::parse_format_string<char>(sv, checker(sv, arg_pack()));
|
||||||
#ifdef FMT_ENFORCE_COMPILE_STRING
|
#ifdef FMT_ENFORCE_COMPILE_STRING
|
||||||
static_assert(
|
static_assert(
|
||||||
FMT_USE_CONSTEVAL && sizeof(s) != 0,
|
FMT_USE_CONSTEVAL && sizeof(s) != 0,
|
||||||
@@ -2711,8 +2769,8 @@ struct formatter<T, Char,
|
|||||||
// Take arguments by lvalue references to avoid some lifetime issues, e.g.
|
// Take arguments by lvalue references to avoid some lifetime issues, e.g.
|
||||||
// auto args = make_format_args(std::string());
|
// auto args = make_format_args(std::string());
|
||||||
template <typename Context = context, typename... T,
|
template <typename Context = context, typename... T,
|
||||||
size_t NUM_ARGS = sizeof...(T),
|
int NUM_ARGS = sizeof...(T),
|
||||||
size_t NUM_NAMED_ARGS = detail::count_named_args<T...>(),
|
int NUM_NAMED_ARGS = detail::count_named_args<T...>(),
|
||||||
unsigned long long DESC = detail::make_descriptor<Context, T...>()>
|
unsigned long long DESC = detail::make_descriptor<Context, T...>()>
|
||||||
constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args)
|
constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args)
|
||||||
-> detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC> {
|
-> detail::format_arg_store<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC> {
|
||||||
@@ -2851,7 +2909,7 @@ FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args);
|
|||||||
template <typename... T>
|
template <typename... T>
|
||||||
FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
|
FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
|
||||||
vargs<T...> va = {{args...}};
|
vargs<T...> va = {{args...}};
|
||||||
if (!detail::use_utf8)
|
if (detail::const_check(!detail::use_utf8))
|
||||||
return detail::vprint_mojibake(stdout, fmt.str, va, false);
|
return detail::vprint_mojibake(stdout, fmt.str, va, false);
|
||||||
return detail::is_locking<T...>() ? vprint_buffered(stdout, fmt.str, va)
|
return detail::is_locking<T...>() ? vprint_buffered(stdout, fmt.str, va)
|
||||||
: vprint(fmt.str, va);
|
: vprint(fmt.str, va);
|
||||||
@@ -2868,7 +2926,8 @@ FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
|
|||||||
template <typename... T>
|
template <typename... T>
|
||||||
FMT_INLINE void print(FILE* f, format_string<T...> fmt, T&&... args) {
|
FMT_INLINE void print(FILE* f, format_string<T...> fmt, T&&... args) {
|
||||||
vargs<T...> va = {{args...}};
|
vargs<T...> va = {{args...}};
|
||||||
if (!detail::use_utf8) return detail::vprint_mojibake(f, fmt.str, va, false);
|
if (detail::const_check(!detail::use_utf8))
|
||||||
|
return detail::vprint_mojibake(f, fmt.str, va, false);
|
||||||
return detail::is_locking<T...>() ? vprint_buffered(f, fmt.str, va)
|
return detail::is_locking<T...>() ? vprint_buffered(f, fmt.str, va)
|
||||||
: vprint(f, fmt.str, va);
|
: vprint(f, fmt.str, va);
|
||||||
}
|
}
|
||||||
@@ -2878,8 +2937,9 @@ FMT_INLINE void print(FILE* f, format_string<T...> fmt, T&&... args) {
|
|||||||
template <typename... T>
|
template <typename... T>
|
||||||
FMT_INLINE void println(FILE* f, format_string<T...> fmt, T&&... args) {
|
FMT_INLINE void println(FILE* f, format_string<T...> fmt, T&&... args) {
|
||||||
vargs<T...> va = {{args...}};
|
vargs<T...> va = {{args...}};
|
||||||
return detail::use_utf8 ? vprintln(f, fmt.str, va)
|
return detail::const_check(detail::use_utf8)
|
||||||
: detail::vprint_mojibake(f, fmt.str, va, true);
|
? vprintln(f, fmt.str, va)
|
||||||
|
: detail::vprint_mojibake(f, fmt.str, va, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Formats `args` according to specifications in `fmt` and writes the output
|
/// Formats `args` according to specifications in `fmt` and writes the output
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ struct is_same_arithmetic_type
|
|||||||
std::is_floating_point<Rep2>::value)> {
|
std::is_floating_point<Rep2>::value)> {
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void throw_duration_error() {
|
FMT_NORETURN inline void throw_duration_error() {
|
||||||
FMT_THROW(format_error("cannot format duration"));
|
FMT_THROW(format_error("cannot format duration"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,24 +540,24 @@ inline auto localtime(std::time_t time) -> std::tm {
|
|||||||
std::time_t time_;
|
std::time_t time_;
|
||||||
std::tm tm_;
|
std::tm tm_;
|
||||||
|
|
||||||
dispatcher(std::time_t t) : time_(t) {}
|
inline dispatcher(std::time_t t) : time_(t) {}
|
||||||
|
|
||||||
auto run() -> bool {
|
inline auto run() -> bool {
|
||||||
using namespace fmt::detail;
|
using namespace fmt::detail;
|
||||||
return handle(localtime_r(&time_, &tm_));
|
return handle(localtime_r(&time_, &tm_));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto handle(std::tm* tm) -> bool { return tm != nullptr; }
|
inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
|
||||||
|
|
||||||
auto handle(detail::null<>) -> bool {
|
inline auto handle(detail::null<>) -> bool {
|
||||||
using namespace fmt::detail;
|
using namespace fmt::detail;
|
||||||
return fallback(localtime_s(&tm_, &time_));
|
return fallback(localtime_s(&tm_, &time_));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fallback(int res) -> bool { return res == 0; }
|
inline auto fallback(int res) -> bool { return res == 0; }
|
||||||
|
|
||||||
#if !FMT_MSC_VERSION
|
#if !FMT_MSC_VERSION
|
||||||
auto fallback(detail::null<>) -> bool {
|
inline auto fallback(detail::null<>) -> bool {
|
||||||
using namespace fmt::detail;
|
using namespace fmt::detail;
|
||||||
std::tm* tm = std::localtime(&time_);
|
std::tm* tm = std::localtime(&time_);
|
||||||
if (tm) tm_ = *tm;
|
if (tm) tm_ = *tm;
|
||||||
@@ -591,24 +591,24 @@ inline auto gmtime(std::time_t time) -> std::tm {
|
|||||||
std::time_t time_;
|
std::time_t time_;
|
||||||
std::tm tm_;
|
std::tm tm_;
|
||||||
|
|
||||||
dispatcher(std::time_t t) : time_(t) {}
|
inline dispatcher(std::time_t t) : time_(t) {}
|
||||||
|
|
||||||
auto run() -> bool {
|
inline auto run() -> bool {
|
||||||
using namespace fmt::detail;
|
using namespace fmt::detail;
|
||||||
return handle(gmtime_r(&time_, &tm_));
|
return handle(gmtime_r(&time_, &tm_));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto handle(std::tm* tm) -> bool { return tm != nullptr; }
|
inline auto handle(std::tm* tm) -> bool { return tm != nullptr; }
|
||||||
|
|
||||||
auto handle(detail::null<>) -> bool {
|
inline auto handle(detail::null<>) -> bool {
|
||||||
using namespace fmt::detail;
|
using namespace fmt::detail;
|
||||||
return fallback(gmtime_s(&tm_, &time_));
|
return fallback(gmtime_s(&tm_, &time_));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto fallback(int res) -> bool { return res == 0; }
|
inline auto fallback(int res) -> bool { return res == 0; }
|
||||||
|
|
||||||
#if !FMT_MSC_VERSION
|
#if !FMT_MSC_VERSION
|
||||||
auto fallback(detail::null<>) -> bool {
|
inline auto fallback(detail::null<>) -> bool {
|
||||||
std::tm* tm = std::gmtime(&time_);
|
std::tm* tm = std::gmtime(&time_);
|
||||||
if (tm) tm_ = *tm;
|
if (tm) tm_ = *tm;
|
||||||
return tm != nullptr;
|
return tm != nullptr;
|
||||||
@@ -912,7 +912,9 @@ template <typename Derived> struct null_chrono_spec_handler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
|
struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
|
||||||
FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
|
FMT_NORETURN inline void unsupported() {
|
||||||
|
FMT_THROW(format_error("no format"));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
||||||
@@ -1069,7 +1071,7 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
|
|||||||
}
|
}
|
||||||
} else if (precision > 0) {
|
} else if (precision > 0) {
|
||||||
*out++ = '.';
|
*out++ = '.';
|
||||||
leading_zeroes = (std::min)(leading_zeroes, precision);
|
leading_zeroes = min_of(leading_zeroes, precision);
|
||||||
int remaining = precision - leading_zeroes;
|
int remaining = precision - leading_zeroes;
|
||||||
out = detail::fill_n(out, leading_zeroes, '0');
|
out = detail::fill_n(out, leading_zeroes, '0');
|
||||||
if (remaining < num_digits) {
|
if (remaining < num_digits) {
|
||||||
@@ -1572,7 +1574,7 @@ class tm_writer {
|
|||||||
struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
|
struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
|
||||||
bool has_precision_integral = false;
|
bool has_precision_integral = false;
|
||||||
|
|
||||||
FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
|
FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no date")); }
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
||||||
@@ -1693,14 +1695,14 @@ class get_locale {
|
|||||||
bool has_locale_ = false;
|
bool has_locale_ = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
|
inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
|
||||||
if (localized)
|
if (localized)
|
||||||
::new (&locale_) std::locale(loc.template get<std::locale>());
|
::new (&locale_) std::locale(loc.template get<std::locale>());
|
||||||
}
|
}
|
||||||
~get_locale() {
|
inline ~get_locale() {
|
||||||
if (has_locale_) locale_.~locale();
|
if (has_locale_) locale_.~locale();
|
||||||
}
|
}
|
||||||
operator const std::locale&() const {
|
inline operator const std::locale&() const {
|
||||||
return has_locale_ ? locale_ : get_classic_locale();
|
return has_locale_ ? locale_ : get_classic_locale();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -42,11 +42,10 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||||
template <typename Char, size_t N,
|
template <typename Char, size_t N, fmt::detail::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;
|
||||||
explicit constexpr operator basic_string_view<char_type>() const {
|
constexpr explicit operator basic_string_view<char_type>() const {
|
||||||
return {Str.data, N - 1};
|
return {Str.data, N - 1};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -525,9 +524,9 @@ FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
|
|||||||
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& fmt, const Args&... args) {
|
void print(std::FILE* f, const S& fmt, const Args&... args) {
|
||||||
memory_buffer buffer;
|
auto buf = memory_buffer();
|
||||||
fmt::format_to(std::back_inserter(buffer), fmt, args...);
|
fmt::format_to(appender(buf), fmt, args...);
|
||||||
detail::print(f, {buffer.data(), buffer.size()});
|
detail::print(f, {buf.data(), buf.size()});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
@@ -538,7 +537,7 @@ void print(const S& fmt, const Args&... args) {
|
|||||||
|
|
||||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||||
inline namespace literals {
|
inline namespace literals {
|
||||||
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
|
template <detail::fixed_string Str> constexpr auto operator""_cf() {
|
||||||
using char_t = remove_cvref_t<decltype(Str.data[0])>;
|
using char_t = remove_cvref_t<decltype(Str.data[0])>;
|
||||||
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
|
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
|
||||||
Str>();
|
Str>();
|
||||||
|
|||||||
@@ -26,6 +26,10 @@
|
|||||||
# include <locale>
|
# include <locale>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef FMT_FUNC
|
||||||
|
# define FMT_FUNC
|
||||||
|
#endif
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
@@ -59,8 +63,8 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
|
|||||||
FMT_ASSERT(out.size() <= inline_buffer_size, "");
|
FMT_ASSERT(out.size() <= inline_buffer_size, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void report_error(format_func func, int error_code,
|
FMT_FUNC void do_report_error(format_func func, int error_code,
|
||||||
const char* message) noexcept {
|
const char* message) noexcept {
|
||||||
memory_buffer full_message;
|
memory_buffer full_message;
|
||||||
func(full_message, error_code, message);
|
func(full_message, error_code, message);
|
||||||
// Don't use fwrite_all because the latter may throw.
|
// Don't use fwrite_all because the latter may throw.
|
||||||
@@ -80,7 +84,7 @@ using std::locale;
|
|||||||
using std::numpunct;
|
using std::numpunct;
|
||||||
using std::use_facet;
|
using std::use_facet;
|
||||||
|
|
||||||
template <typename Locale>
|
template <typename Locale, enable_if_t<(sizeof(Locale::collate) != 0), int>>
|
||||||
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
|
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
|
||||||
static_assert(std::is_same<Locale, locale>::value, "");
|
static_assert(std::is_same<Locale, locale>::value, "");
|
||||||
}
|
}
|
||||||
@@ -130,7 +134,9 @@ FMT_FUNC auto write_loc(appender out, loc_value value,
|
|||||||
|
|
||||||
FMT_FUNC void report_error(const char* message) {
|
FMT_FUNC void report_error(const char* message) {
|
||||||
#if FMT_USE_EXCEPTIONS
|
#if FMT_USE_EXCEPTIONS
|
||||||
throw format_error(message);
|
// Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
|
||||||
|
// from MSVC.
|
||||||
|
FMT_THROW(format_error(message));
|
||||||
#else
|
#else
|
||||||
fputs(message, stderr);
|
fputs(message, stderr);
|
||||||
abort();
|
abort();
|
||||||
@@ -1430,7 +1436,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
|
|||||||
|
|
||||||
FMT_FUNC void report_system_error(int error_code,
|
FMT_FUNC void report_system_error(int error_code,
|
||||||
const char* message) noexcept {
|
const char* message) noexcept {
|
||||||
report_error(format_system_error, error_code, message);
|
do_report_error(format_system_error, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
|
FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -176,24 +176,24 @@ class buffered_file {
|
|||||||
|
|
||||||
friend class file;
|
friend class file;
|
||||||
|
|
||||||
explicit buffered_file(FILE* f) : file_(f) {}
|
inline explicit buffered_file(FILE* f) : file_(f) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
buffered_file(const buffered_file&) = delete;
|
buffered_file(const buffered_file&) = delete;
|
||||||
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() noexcept : file_(nullptr) {}
|
inline 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() noexcept;
|
FMT_API ~buffered_file() noexcept;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
|
inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
|
||||||
other.file_ = nullptr;
|
other.file_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator=(buffered_file&& other) -> buffered_file& {
|
inline auto operator=(buffered_file&& other) -> buffered_file& {
|
||||||
close();
|
close();
|
||||||
file_ = other.file_;
|
file_ = other.file_;
|
||||||
other.file_ = nullptr;
|
other.file_ = nullptr;
|
||||||
@@ -207,7 +207,7 @@ 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.
|
||||||
auto get() const noexcept -> FILE* { return file_; }
|
inline auto get() const noexcept -> FILE* { return file_; }
|
||||||
|
|
||||||
FMT_API auto descriptor() const -> int;
|
FMT_API auto descriptor() const -> int;
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ class FMT_API file {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Constructs a file object which doesn't represent any file.
|
// Constructs a file object which doesn't represent any file.
|
||||||
file() noexcept : fd_(-1) {}
|
inline 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.
|
||||||
file(cstring_view path, int oflag);
|
file(cstring_view path, int oflag);
|
||||||
@@ -257,10 +257,10 @@ class FMT_API file {
|
|||||||
file(const file&) = delete;
|
file(const file&) = delete;
|
||||||
void operator=(const file&) = delete;
|
void operator=(const file&) = delete;
|
||||||
|
|
||||||
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
|
inline 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.
|
||||||
auto operator=(file&& other) -> file& {
|
inline auto operator=(file&& other) -> file& {
|
||||||
close();
|
close();
|
||||||
fd_ = other.fd_;
|
fd_ = other.fd_;
|
||||||
other.fd_ = -1;
|
other.fd_ = -1;
|
||||||
@@ -271,7 +271,7 @@ class FMT_API file {
|
|||||||
~file() noexcept;
|
~file() noexcept;
|
||||||
|
|
||||||
// Returns the file descriptor.
|
// Returns the file descriptor.
|
||||||
auto descriptor() const noexcept -> int { return fd_; }
|
inline auto descriptor() const noexcept -> int { return fd_; }
|
||||||
|
|
||||||
// Closes the file.
|
// Closes the file.
|
||||||
void close();
|
void close();
|
||||||
@@ -324,9 +324,9 @@ auto getpagesize() -> long;
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
struct buffer_size {
|
struct buffer_size {
|
||||||
buffer_size() = default;
|
constexpr buffer_size() = default;
|
||||||
size_t value = 0;
|
size_t value = 0;
|
||||||
auto operator=(size_t val) const -> buffer_size {
|
FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size {
|
||||||
auto bs = buffer_size();
|
auto bs = buffer_size();
|
||||||
bs.value = val;
|
bs.value = val;
|
||||||
return bs;
|
return bs;
|
||||||
@@ -337,7 +337,7 @@ struct ostream_params {
|
|||||||
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
|
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
|
||||||
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||||
|
|
||||||
ostream_params() {}
|
constexpr ostream_params() {}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
|
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
|
||||||
@@ -381,7 +381,7 @@ class FMT_API ostream : private detail::buffer<char> {
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush() {
|
inline void flush() {
|
||||||
if (size() == 0) return;
|
if (size() == 0) return;
|
||||||
file_.write(data(), size() * sizeof(data()[0]));
|
file_.write(data(), size() * sizeof(data()[0]));
|
||||||
clear();
|
clear();
|
||||||
@@ -390,7 +390,7 @@ class FMT_API ostream : private detail::buffer<char> {
|
|||||||
template <typename... T>
|
template <typename... T>
|
||||||
friend auto output_file(cstring_view path, T... params) -> ostream;
|
friend auto output_file(cstring_view path, T... params) -> ostream;
|
||||||
|
|
||||||
void close() {
|
inline void close() {
|
||||||
flush();
|
flush();
|
||||||
file_.close();
|
file_.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,14 @@
|
|||||||
|
|
||||||
#include "chrono.h" // formatbuf
|
#include "chrono.h" // formatbuf
|
||||||
|
|
||||||
|
#ifdef _MSVC_STL_UPDATE
|
||||||
|
# define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE
|
||||||
|
#elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5
|
||||||
|
# define FMT_MSVC_STL_UPDATE _MSVC_LANG
|
||||||
|
#else
|
||||||
|
# define FMT_MSVC_STL_UPDATE 0
|
||||||
|
#endif
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
@@ -35,7 +43,7 @@ class file_access {
|
|||||||
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
|
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#if FMT_MSC_VERSION
|
#if FMT_MSVC_STL_UPDATE
|
||||||
template class file_access<file_access_tag, std::filebuf,
|
template class file_access<file_access_tag, std::filebuf,
|
||||||
&std::filebuf::_Myfile>;
|
&std::filebuf::_Myfile>;
|
||||||
auto get_file(std::filebuf&) -> FILE*;
|
auto get_file(std::filebuf&) -> FILE*;
|
||||||
@@ -109,7 +117,7 @@ inline void vprint(std::ostream& os, string_view fmt, format_args args) {
|
|||||||
auto buffer = memory_buffer();
|
auto buffer = memory_buffer();
|
||||||
detail::vformat_to(buffer, fmt, args);
|
detail::vformat_to(buffer, fmt, args);
|
||||||
FILE* f = nullptr;
|
FILE* f = nullptr;
|
||||||
#if FMT_MSC_VERSION && FMT_USE_RTTI
|
#if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI
|
||||||
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
|
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
|
||||||
f = detail::get_file(*buf);
|
f = detail::get_file(*buf);
|
||||||
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
|
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ template <bool IsSigned> struct int_checker {
|
|||||||
unsigned max = to_unsigned(max_value<int>());
|
unsigned max = to_unsigned(max_value<int>());
|
||||||
return value <= max;
|
return value <= max;
|
||||||
}
|
}
|
||||||
static auto fits_in_int(bool) -> bool { return true; }
|
inline static auto fits_in_int(bool) -> bool { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <> struct int_checker<true> {
|
template <> struct int_checker<true> {
|
||||||
@@ -87,7 +87,7 @@ template <> struct int_checker<true> {
|
|||||||
return value >= (std::numeric_limits<int>::min)() &&
|
return value >= (std::numeric_limits<int>::min)() &&
|
||||||
value <= max_value<int>();
|
value <= max_value<int>();
|
||||||
}
|
}
|
||||||
static auto fits_in_int(int) -> bool { return true; }
|
inline static auto fits_in_int(int) -> bool { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct printf_precision_handler {
|
struct printf_precision_handler {
|
||||||
@@ -205,7 +205,7 @@ class printf_width_handler {
|
|||||||
format_specs& specs_;
|
format_specs& specs_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
|
inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
auto operator()(T value) -> unsigned {
|
auto operator()(T value) -> unsigned {
|
||||||
|
|||||||
@@ -357,12 +357,9 @@ template <typename R>
|
|||||||
using maybe_const_range =
|
using maybe_const_range =
|
||||||
conditional_t<has_const_begin_end<R>::value, const R, R>;
|
conditional_t<has_const_begin_end<R>::value, const R, R>;
|
||||||
|
|
||||||
// Workaround a bug in MSVC 2015 and earlier.
|
|
||||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
|
||||||
template <typename R, typename Char>
|
template <typename R, typename Char>
|
||||||
struct is_formattable_delayed
|
struct is_formattable_delayed
|
||||||
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
|
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
|
||||||
#endif
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template <typename...> struct conjunction : std::true_type {};
|
template <typename...> struct conjunction : std::true_type {};
|
||||||
@@ -498,13 +495,8 @@ struct formatter<
|
|||||||
range_format_kind<R, Char>::value != range_format::disabled &&
|
range_format_kind<R, Char>::value != range_format::disabled &&
|
||||||
range_format_kind<R, Char>::value != range_format::map &&
|
range_format_kind<R, Char>::value != range_format::map &&
|
||||||
range_format_kind<R, Char>::value != range_format::string &&
|
range_format_kind<R, Char>::value != range_format::string &&
|
||||||
range_format_kind<R, Char>::value != range_format::debug_string>
|
range_format_kind<R, Char>::value != range_format::debug_string>,
|
||||||
// Workaround a bug in MSVC 2015 and earlier.
|
detail::is_formattable_delayed<R, Char>>::value>> {
|
||||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
|
||||||
,
|
|
||||||
detail::is_formattable_delayed<R, Char>
|
|
||||||
#endif
|
|
||||||
>::value>> {
|
|
||||||
private:
|
private:
|
||||||
using range_type = detail::maybe_const_range<R>;
|
using range_type = detail::maybe_const_range<R>;
|
||||||
range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
|
range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
|
||||||
@@ -646,9 +638,9 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
|
|||||||
#endif
|
#endif
|
||||||
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
|
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
|
||||||
|
|
||||||
using view_ref = conditional_t<std::is_copy_constructible<It>::value,
|
using view = conditional_t<std::is_copy_constructible<It>::value,
|
||||||
const join_view<It, Sentinel, Char>&,
|
const join_view<It, Sentinel, Char>,
|
||||||
join_view<It, Sentinel, Char>&&>;
|
join_view<It, Sentinel, Char>>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using nonlocking = void;
|
using nonlocking = void;
|
||||||
@@ -658,9 +650,10 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(view_ref& value, FormatContext& ctx) const
|
auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||||
-> decltype(ctx.out()) {
|
using iter =
|
||||||
auto it = std::forward<view_ref>(value).begin;
|
conditional_t<std::is_copy_constructible<view>::value, It, It&>;
|
||||||
|
iter it = value.begin;
|
||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
if (it == value.end) return out;
|
if (it == value.end) return out;
|
||||||
out = value_formatter_.format(*it, ctx);
|
out = value_formatter_.format(*it, ctx);
|
||||||
@@ -675,39 +668,11 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns a view that formats the iterator range `[begin, end)` with elements
|
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
|
||||||
/// separated by `sep`.
|
const Tuple& tuple;
|
||||||
template <typename It, typename Sentinel>
|
|
||||||
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
|
|
||||||
return {std::move(begin), end, sep};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a view that formats `range` with elements separated by `sep`.
|
|
||||||
*
|
|
||||||
* **Example**:
|
|
||||||
*
|
|
||||||
* auto v = std::vector<int>{1, 2, 3};
|
|
||||||
* fmt::print("{}", fmt::join(v, ", "));
|
|
||||||
* // Output: 1, 2, 3
|
|
||||||
*
|
|
||||||
* `fmt::join` applies passed format specifiers to the range elements:
|
|
||||||
*
|
|
||||||
* fmt::print("{:02}", fmt::join(v, ", "));
|
|
||||||
* // Output: 01, 02, 03
|
|
||||||
*/
|
|
||||||
template <typename Range>
|
|
||||||
auto join(Range&& r, string_view sep)
|
|
||||||
-> join_view<decltype(detail::range_begin(r)),
|
|
||||||
decltype(detail::range_end(r))> {
|
|
||||||
return {detail::range_begin(r), detail::range_end(r), sep};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
|
||||||
const std::tuple<T...>& tuple;
|
|
||||||
basic_string_view<Char> sep;
|
basic_string_view<Char> sep;
|
||||||
|
|
||||||
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
|
tuple_join_view(const Tuple& t, basic_string_view<Char> s)
|
||||||
: tuple(t), sep{s} {}
|
: tuple(t), sep{s} {}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -718,21 +683,22 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
|||||||
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename Char, typename... T>
|
template <typename Char, typename Tuple>
|
||||||
struct formatter<tuple_join_view<Char, T...>, Char> {
|
struct formatter<tuple_join_view<Char, Tuple>, Char,
|
||||||
|
enable_if_t<is_tuple_like<Tuple>::value>> {
|
||||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||||
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
|
return do_parse(ctx, std::tuple_size<Tuple>());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const tuple_join_view<Char, T...>& value,
|
auto format(const tuple_join_view<Char, Tuple>& value,
|
||||||
FormatContext& ctx) const -> typename FormatContext::iterator {
|
FormatContext& ctx) const -> typename FormatContext::iterator {
|
||||||
return do_format(value, ctx,
|
return do_format(value, ctx, std::tuple_size<Tuple>());
|
||||||
std::integral_constant<size_t, sizeof...(T)>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
|
decltype(detail::tuple::get_formatters<Tuple, Char>(
|
||||||
|
detail::tuple_index_sequence<Tuple>())) formatters_;
|
||||||
|
|
||||||
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
|
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
|
||||||
std::integral_constant<size_t, 0>)
|
std::integral_constant<size_t, 0>)
|
||||||
@@ -746,7 +712,7 @@ struct formatter<tuple_join_view<Char, T...>, Char> {
|
|||||||
-> const Char* {
|
-> const Char* {
|
||||||
auto end = ctx.begin();
|
auto end = ctx.begin();
|
||||||
#if FMT_TUPLE_JOIN_SPECIFIERS
|
#if FMT_TUPLE_JOIN_SPECIFIERS
|
||||||
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
|
end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
|
||||||
if (N > 1) {
|
if (N > 1) {
|
||||||
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
|
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
|
||||||
if (end != end1)
|
if (end != end1)
|
||||||
@@ -757,18 +723,20 @@ struct formatter<tuple_join_view<Char, T...>, Char> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
|
auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
|
||||||
std::integral_constant<size_t, 0>) const ->
|
std::integral_constant<size_t, 0>) const ->
|
||||||
typename FormatContext::iterator {
|
typename FormatContext::iterator {
|
||||||
return ctx.out();
|
return ctx.out();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext, size_t N>
|
template <typename FormatContext, size_t N>
|
||||||
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
|
||||||
std::integral_constant<size_t, N>) const ->
|
std::integral_constant<size_t, N>) const ->
|
||||||
typename FormatContext::iterator {
|
typename FormatContext::iterator {
|
||||||
auto out = std::get<sizeof...(T) - N>(formatters_)
|
using std::get;
|
||||||
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
|
auto out =
|
||||||
|
std::get<std::tuple_size<Tuple>::value - N>(formatters_)
|
||||||
|
.format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
|
||||||
if (N <= 1) return out;
|
if (N <= 1) return out;
|
||||||
out = detail::copy<Char>(value.sep, out);
|
out = detail::copy<Char>(value.sep, out);
|
||||||
ctx.advance_to(out);
|
ctx.advance_to(out);
|
||||||
@@ -816,6 +784,34 @@ struct formatter<
|
|||||||
|
|
||||||
FMT_BEGIN_EXPORT
|
FMT_BEGIN_EXPORT
|
||||||
|
|
||||||
|
/// Returns a view that formats the iterator range `[begin, end)` with elements
|
||||||
|
/// separated by `sep`.
|
||||||
|
template <typename It, typename Sentinel>
|
||||||
|
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
|
||||||
|
return {std::move(begin), end, sep};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a view that formats `range` with elements separated by `sep`.
|
||||||
|
*
|
||||||
|
* **Example**:
|
||||||
|
*
|
||||||
|
* auto v = std::vector<int>{1, 2, 3};
|
||||||
|
* fmt::print("{}", fmt::join(v, ", "));
|
||||||
|
* // Output: 1, 2, 3
|
||||||
|
*
|
||||||
|
* `fmt::join` applies passed format specifiers to the range elements:
|
||||||
|
*
|
||||||
|
* fmt::print("{:02}", fmt::join(v, ", "));
|
||||||
|
* // Output: 01, 02, 03
|
||||||
|
*/
|
||||||
|
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
|
||||||
|
auto join(Range&& r, string_view sep)
|
||||||
|
-> join_view<decltype(detail::range_begin(r)),
|
||||||
|
decltype(detail::range_end(r))> {
|
||||||
|
return {detail::range_begin(r), detail::range_end(r), sep};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an object that formats `std::tuple` with elements separated by `sep`.
|
* Returns an object that formats `std::tuple` with elements separated by `sep`.
|
||||||
*
|
*
|
||||||
@@ -825,9 +821,9 @@ FMT_BEGIN_EXPORT
|
|||||||
* fmt::print("{}", fmt::join(t, ", "));
|
* fmt::print("{}", fmt::join(t, ", "));
|
||||||
* // Output: 1, a
|
* // Output: 1, a
|
||||||
*/
|
*/
|
||||||
template <typename... T>
|
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
|
||||||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
|
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
|
||||||
-> tuple_join_view<char, T...> {
|
-> tuple_join_view<char, Tuple> {
|
||||||
return {tuple, sep};
|
return {tuple, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,8 @@
|
|||||||
|
|
||||||
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
|
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
|
||||||
# if FMT_CPLUSPLUS >= 201703L
|
# if FMT_CPLUSPLUS >= 201703L
|
||||||
# if FMT_HAS_INCLUDE(<filesystem>)
|
# if FMT_HAS_INCLUDE(<filesystem>) && \
|
||||||
|
(!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0)
|
||||||
# include <filesystem>
|
# include <filesystem>
|
||||||
# endif
|
# endif
|
||||||
# if FMT_HAS_INCLUDE(<variant>)
|
# if FMT_HAS_INCLUDE(<variant>)
|
||||||
@@ -183,7 +184,8 @@ FMT_END_NAMESPACE
|
|||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
FMT_EXPORT
|
FMT_EXPORT
|
||||||
template <std::size_t N, typename Char>
|
template <std::size_t N, typename Char>
|
||||||
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
|
struct formatter<std::bitset<N>, Char>
|
||||||
|
: nested_formatter<basic_string_view<Char>, Char> {
|
||||||
private:
|
private:
|
||||||
// Functor because C++11 doesn't support generic lambdas.
|
// Functor because C++11 doesn't support generic lambdas.
|
||||||
struct writer {
|
struct writer {
|
||||||
@@ -203,7 +205,7 @@ struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
|
|||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
|
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
|
||||||
-> decltype(ctx.out()) {
|
-> decltype(ctx.out()) {
|
||||||
return write_padded(ctx, writer{bs});
|
return this->write_padded(ctx, writer{bs});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -433,8 +435,8 @@ template <> struct formatter<std::error_code> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
FMT_CONSTEXPR20 auto format(const std::error_code& ec, FormatContext& ctx) const
|
FMT_CONSTEXPR20 auto format(const std::error_code& ec,
|
||||||
-> decltype(ctx.out()) {
|
FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||||
auto specs = specs_;
|
auto specs = specs_;
|
||||||
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
|
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
|
||||||
ctx);
|
ctx);
|
||||||
@@ -694,9 +696,7 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
|
|||||||
|
|
||||||
auto outer_specs = format_specs();
|
auto outer_specs = format_specs();
|
||||||
outer_specs.width = specs.width;
|
outer_specs.width = specs.width;
|
||||||
auto fill = specs.template fill<Char>();
|
outer_specs.set_fill(specs);
|
||||||
if (fill)
|
|
||||||
outer_specs.set_fill(basic_string_view<Char>(fill, specs.fill_size()));
|
|
||||||
outer_specs.set_align(specs.align());
|
outer_specs.set_align(specs.align());
|
||||||
|
|
||||||
specs.width = 0;
|
specs.width = 0;
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ auto join(It begin, Sentinel end, wstring_view sep)
|
|||||||
return {begin, end, sep};
|
return {begin, end, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Range>
|
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
|
||||||
auto join(Range&& range, wstring_view sep)
|
auto join(Range&& range, wstring_view sep)
|
||||||
-> join_view<decltype(std::begin(range)), decltype(std::end(range)),
|
-> join_view<decltype(std::begin(range)), decltype(std::end(range)),
|
||||||
wchar_t> {
|
wchar_t> {
|
||||||
@@ -153,9 +153,9 @@ auto join(std::initializer_list<T> list, wstring_view sep)
|
|||||||
return join(std::begin(list), std::end(list), sep);
|
return join(std::begin(list), std::end(list), sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
|
||||||
auto join(const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep)
|
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
|
||||||
-> tuple_join_view<wchar_t, T...> {
|
-> tuple_join_view<wchar_t, Tuple> {
|
||||||
return {tuple, sep};
|
return {tuple, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,11 +191,9 @@ auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
|
|||||||
fmt::make_format_args<buffered_context<Char>>(args...));
|
fmt::make_format_args<buffered_context<Char>>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Locale, typename S,
|
template <typename S, typename Char = detail::format_string_char_t<S>,
|
||||||
typename Char = detail::format_string_char_t<S>,
|
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
|
||||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
inline auto vformat(detail::locale_ref loc, const S& fmt,
|
||||||
detail::is_exotic_char<Char>::value)>
|
|
||||||
inline auto vformat(const Locale& loc, const S& fmt,
|
|
||||||
typename detail::vformat_args<Char>::type args)
|
typename detail::vformat_args<Char>::type args)
|
||||||
-> std::basic_string<Char> {
|
-> std::basic_string<Char> {
|
||||||
auto buf = basic_memory_buffer<Char>();
|
auto buf = basic_memory_buffer<Char>();
|
||||||
@@ -204,11 +202,10 @@ inline auto vformat(const Locale& loc, const S& fmt,
|
|||||||
return {buf.data(), buf.size()};
|
return {buf.data(), buf.size()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Locale, typename S, typename... T,
|
template <typename S, typename... T,
|
||||||
typename Char = detail::format_string_char_t<S>,
|
typename Char = detail::format_string_char_t<S>,
|
||||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
|
||||||
detail::is_exotic_char<Char>::value)>
|
inline auto format(detail::locale_ref loc, const S& fmt, T&&... args)
|
||||||
inline auto format(const Locale& loc, const S& fmt, T&&... args)
|
|
||||||
-> std::basic_string<Char> {
|
-> std::basic_string<Char> {
|
||||||
return vformat(loc, detail::to_string_view(fmt),
|
return vformat(loc, detail::to_string_view(fmt),
|
||||||
fmt::make_format_args<buffered_context<Char>>(args...));
|
fmt::make_format_args<buffered_context<Char>>(args...));
|
||||||
@@ -235,12 +232,11 @@ inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
|
|||||||
fmt::make_format_args<buffered_context<Char>>(args...));
|
fmt::make_format_args<buffered_context<Char>>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Locale, typename S, typename OutputIt, typename... Args,
|
template <typename S, typename OutputIt, typename... Args,
|
||||||
typename Char = detail::format_string_char_t<S>,
|
typename Char = detail::format_string_char_t<S>,
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
detail::is_locale<Locale>::value&&
|
detail::is_exotic_char<Char>::value)>
|
||||||
detail::is_exotic_char<Char>::value)>
|
inline auto vformat_to(OutputIt out, detail::locale_ref loc, const S& fmt,
|
||||||
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
|
|
||||||
typename detail::vformat_args<Char>::type args)
|
typename detail::vformat_args<Char>::type args)
|
||||||
-> OutputIt {
|
-> OutputIt {
|
||||||
auto&& buf = detail::get_buffer<Char>(out);
|
auto&& buf = detail::get_buffer<Char>(out);
|
||||||
@@ -248,12 +244,11 @@ inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
|
|||||||
return detail::get_iterator(buf, out);
|
return detail::get_iterator(buf, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Locale, typename S, typename... T,
|
template <typename OutputIt, typename S, typename... T,
|
||||||
typename Char = detail::format_string_char_t<S>,
|
typename Char = detail::format_string_char_t<S>,
|
||||||
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
|
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
|
||||||
detail::is_locale<Locale>::value &&
|
|
||||||
detail::is_exotic_char<Char>::value>
|
detail::is_exotic_char<Char>::value>
|
||||||
inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
|
inline auto format_to(OutputIt out, detail::locale_ref loc, const S& fmt,
|
||||||
T&&... args) ->
|
T&&... args) ->
|
||||||
typename std::enable_if<enable, OutputIt>::type {
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
return vformat_to(out, loc, detail::to_string_view(fmt),
|
return vformat_to(out, loc, detail::to_string_view(fmt),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ template FMT_API auto dragonbox::to_decimal(double x) noexcept
|
|||||||
-> dragonbox::decimal_fp<double>;
|
-> dragonbox::decimal_fp<double>;
|
||||||
|
|
||||||
#if FMT_USE_LOCALE
|
#if FMT_USE_LOCALE
|
||||||
|
// DEPRECATED! locale_ref in the detail namespace
|
||||||
template FMT_API locale_ref::locale_ref(const std::locale& loc);
|
template FMT_API locale_ref::locale_ref(const std::locale& loc);
|
||||||
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
|
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void report_windows_error(int error_code, const char* message) noexcept {
|
void report_windows_error(int error_code, const char* message) noexcept {
|
||||||
report_error(detail::format_windows_error, error_code, message);
|
do_report_error(detail::format_windows_error, error_code, message);
|
||||||
}
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
"""Manage site and releases.
|
"""Make a release.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
manage.py release [<branch>]
|
release.py [<branch>]
|
||||||
manage.py site
|
|
||||||
|
|
||||||
For the release command $FMT_TOKEN should contain a GitHub personal access token
|
For the release command $FMT_TOKEN should contain a GitHub personal access token
|
||||||
obtained from https://github.com/settings/tokens.
|
obtained from https://github.com/settings/tokens.
|
||||||
@@ -12,9 +11,9 @@ obtained from https://github.com/settings/tokens.
|
|||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import datetime, docopt, errno, fileinput, json, os
|
import datetime, docopt, errno, fileinput, json, os
|
||||||
import re, requests, shutil, sys
|
import re, shutil, sys
|
||||||
from contextlib import contextmanager
|
|
||||||
from subprocess import check_call
|
from subprocess import check_call
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
|
||||||
class Git:
|
class Git:
|
||||||
@@ -81,46 +80,15 @@ def create_build_env():
|
|||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
fmt_repo_url = 'git@github.com:fmtlib/fmt'
|
if __name__ == '__main__':
|
||||||
|
args = docopt.docopt(__doc__)
|
||||||
|
|
||||||
def update_site(env):
|
|
||||||
env.fmt_repo.update(fmt_repo_url)
|
|
||||||
|
|
||||||
doc_repo = Git(os.path.join(env.build_dir, 'fmt.dev'))
|
|
||||||
doc_repo.update('git@github.com:fmtlib/fmt.dev')
|
|
||||||
|
|
||||||
version = '11.0.0'
|
|
||||||
clean_checkout(env.fmt_repo, version)
|
|
||||||
target_doc_dir = os.path.join(env.fmt_repo.dir, 'doc')
|
|
||||||
|
|
||||||
# Build the docs.
|
|
||||||
html_dir = os.path.join(env.build_dir, 'html')
|
|
||||||
if os.path.exists(html_dir):
|
|
||||||
shutil.rmtree(html_dir)
|
|
||||||
include_dir = env.fmt_repo.dir
|
|
||||||
import build
|
|
||||||
build.build_docs(version, doc_dir=target_doc_dir,
|
|
||||||
include_dir=include_dir, work_dir=env.build_dir)
|
|
||||||
shutil.rmtree(os.path.join(html_dir, '.doctrees'))
|
|
||||||
# Copy docs to the website.
|
|
||||||
version_doc_dir = os.path.join(doc_repo.dir, version)
|
|
||||||
try:
|
|
||||||
shutil.rmtree(version_doc_dir)
|
|
||||||
except OSError as e:
|
|
||||||
if e.errno != errno.ENOENT:
|
|
||||||
raise
|
|
||||||
shutil.move(html_dir, version_doc_dir)
|
|
||||||
|
|
||||||
|
|
||||||
def release(args):
|
|
||||||
env = create_build_env()
|
env = create_build_env()
|
||||||
fmt_repo = env.fmt_repo
|
fmt_repo = env.fmt_repo
|
||||||
|
|
||||||
branch = args.get('<branch>')
|
branch = args.get('<branch>')
|
||||||
if branch is None:
|
if branch is None:
|
||||||
branch = 'master'
|
branch = 'master'
|
||||||
if not fmt_repo.update('-b', branch, fmt_repo_url):
|
if not fmt_repo.update('-b', branch, 'git@github.com:fmtlib/fmt'):
|
||||||
clean_checkout(fmt_repo, branch)
|
clean_checkout(fmt_repo, branch)
|
||||||
|
|
||||||
# Update the date in the changelog and extract the version and the first
|
# Update the date in the changelog and extract the version and the first
|
||||||
@@ -191,28 +159,30 @@ def release(args):
|
|||||||
# Create a release on GitHub.
|
# Create a release on GitHub.
|
||||||
fmt_repo.push('origin', 'release')
|
fmt_repo.push('origin', 'release')
|
||||||
auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')}
|
auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')}
|
||||||
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
|
req = urllib.request.Request(
|
||||||
headers=auth_headers,
|
'https://api.github.com/repos/fmtlib/fmt/releases',
|
||||||
data=json.dumps({'tag_name': version,
|
data=json.dumps({'tag_name': version,
|
||||||
'target_commitish': 'release',
|
'target_commitish': 'release',
|
||||||
'body': changes, 'draft': True}))
|
'body': changes, 'draft': True}).encode('utf-8'),
|
||||||
if r.status_code != 201:
|
headers=auth_headers, method='POST')
|
||||||
raise Exception('Failed to create a release ' + str(r))
|
with urllib.request.urlopen(req) as response:
|
||||||
id = r.json()['id']
|
if response.status != 201:
|
||||||
|
raise Exception(f'Failed to create a release ' +
|
||||||
|
'{response.status} {response.reason}')
|
||||||
|
response_data = json.loads(response.read().decode('utf-8'))
|
||||||
|
id = response_data['id']
|
||||||
|
|
||||||
|
# Upload the package.
|
||||||
uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases'
|
uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases'
|
||||||
package = 'fmt-{}.zip'.format(version)
|
package = 'fmt-{}.zip'.format(version)
|
||||||
r = requests.post(
|
req = urllib.request.Request(
|
||||||
'{}/{}/assets?name={}'.format(uploads_url, id, package),
|
f'{uploads_url}/{id}/assets?name={package}',
|
||||||
headers={'Content-Type': 'application/zip'} | auth_headers,
|
headers={'Content-Type': 'application/zip'} | auth_headers,
|
||||||
data=open('build/fmt/' + package, 'rb'))
|
data=open('build/fmt/' + package, 'rb').read(), method='POST')
|
||||||
if r.status_code != 201:
|
with urllib.request.urlopen(req) as response:
|
||||||
raise Exception('Failed to upload an asset ' + str(r))
|
if response.status != 201:
|
||||||
|
raise Exception(f'Failed to upload an asset '
|
||||||
|
'{response.status} {response.reason}')
|
||||||
|
|
||||||
update_site(env)
|
short_version = '.'.join(version.split('.')[:-1])
|
||||||
|
check_call(['./mkdocs', 'deploy', short_version])
|
||||||
if __name__ == '__main__':
|
|
||||||
args = docopt.docopt(__doc__)
|
|
||||||
if args.get('release'):
|
|
||||||
release(args)
|
|
||||||
elif args.get('site'):
|
|
||||||
update_site(create_build_env())
|
|
||||||
@@ -62,13 +62,14 @@ if (NOT (MSVC AND BUILD_SHARED_LIBS))
|
|||||||
endif ()
|
endif ()
|
||||||
add_fmt_test(ostream-test)
|
add_fmt_test(ostream-test)
|
||||||
add_fmt_test(compile-test)
|
add_fmt_test(compile-test)
|
||||||
add_fmt_test(compile-fp-test HEADER_ONLY)
|
add_fmt_test(compile-fp-test)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
# Without this option, MSVC returns 199711L for the __cplusplus macro.
|
# Without this option, MSVC returns 199711L for the __cplusplus macro.
|
||||||
target_compile_options(compile-fp-test PRIVATE /Zc:__cplusplus)
|
target_compile_options(compile-fp-test PRIVATE /Zc:__cplusplus)
|
||||||
endif()
|
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(no-builtin-types-test HEADER_ONLY)
|
||||||
|
|
||||||
add_fmt_test(scan-test HEADER_ONLY)
|
add_fmt_test(scan-test HEADER_ONLY)
|
||||||
check_symbol_exists(strptime "time.h" HAVE_STRPTIME)
|
check_symbol_exists(strptime "time.h" HAVE_STRPTIME)
|
||||||
|
|||||||
@@ -186,3 +186,17 @@ TEST(args_test, move_constructor) {
|
|||||||
store.reset();
|
store.reset();
|
||||||
EXPECT_EQ(fmt::vformat("{} {} {a1}", moved_store), "42 foo foo");
|
EXPECT_EQ(fmt::vformat("{} {} {a1}", moved_store), "42 foo foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(args_test, size) {
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
EXPECT_EQ(store.size(), 0);
|
||||||
|
|
||||||
|
store.push_back(42);
|
||||||
|
EXPECT_EQ(store.size(), 1);
|
||||||
|
|
||||||
|
store.push_back("Molybdenum");
|
||||||
|
EXPECT_EQ(store.size(), 2);
|
||||||
|
|
||||||
|
store.clear();
|
||||||
|
EXPECT_EQ(store.size(), 0);
|
||||||
|
}
|
||||||
|
|||||||
@@ -645,9 +645,7 @@ TEST(base_test, is_formattable) {
|
|||||||
EXPECT_TRUE(fmt::is_formattable<const const_formattable&>::value);
|
EXPECT_TRUE(fmt::is_formattable<const const_formattable&>::value);
|
||||||
|
|
||||||
EXPECT_TRUE(fmt::is_formattable<nonconst_formattable&>::value);
|
EXPECT_TRUE(fmt::is_formattable<nonconst_formattable&>::value);
|
||||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
|
||||||
EXPECT_FALSE(fmt::is_formattable<const nonconst_formattable&>::value);
|
EXPECT_FALSE(fmt::is_formattable<const nonconst_formattable&>::value);
|
||||||
#endif
|
|
||||||
|
|
||||||
EXPECT_FALSE(fmt::is_formattable<convertible_to_pointer>::value);
|
EXPECT_FALSE(fmt::is_formattable<convertible_to_pointer>::value);
|
||||||
const auto f = convertible_to_pointer_formattable();
|
const auto f = convertible_to_pointer_formattable();
|
||||||
@@ -745,19 +743,6 @@ TEST(base_test, no_implicit_conversion_to_string_view) {
|
|||||||
fmt::is_formattable<implicitly_convertible_to_string_view>::value);
|
fmt::is_formattable<implicitly_convertible_to_string_view>::value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FMT_USE_STRING_VIEW
|
|
||||||
struct implicitly_convertible_to_std_string_view {
|
|
||||||
operator std::string_view() const { return "foo"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(base_test, no_implicit_conversion_to_std_string_view) {
|
|
||||||
EXPECT_FALSE(
|
|
||||||
fmt::is_formattable<implicitly_convertible_to_std_string_view>::value);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// std::is_constructible is broken in MSVC until version 2015.
|
|
||||||
#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"; }
|
||||||
};
|
};
|
||||||
@@ -769,7 +754,16 @@ TEST(base_test, format_explicitly_convertible_to_string_view) {
|
|||||||
!fmt::is_formattable<explicitly_convertible_to_string_view>::value, "");
|
!fmt::is_formattable<explicitly_convertible_to_string_view>::value, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
# ifdef FMT_USE_STRING_VIEW
|
#if FMT_CPLUSPLUS >= 201703L
|
||||||
|
struct implicitly_convertible_to_std_string_view {
|
||||||
|
operator std::string_view() const { return "foo"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(base_test, no_implicit_conversion_to_std_string_view) {
|
||||||
|
EXPECT_FALSE(
|
||||||
|
fmt::is_formattable<implicitly_convertible_to_std_string_view>::value);
|
||||||
|
}
|
||||||
|
|
||||||
struct explicitly_convertible_to_std_string_view {
|
struct explicitly_convertible_to_std_string_view {
|
||||||
explicit operator std::string_view() const { return "foo"; }
|
explicit operator std::string_view() const { return "foo"; }
|
||||||
};
|
};
|
||||||
@@ -781,8 +775,7 @@ TEST(base_test, format_explicitly_convertible_to_std_string_view) {
|
|||||||
!fmt::is_formattable<explicitly_convertible_to_std_string_view>::value,
|
!fmt::is_formattable<explicitly_convertible_to_std_string_view>::value,
|
||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
# endif
|
#endif // FMT_CPLUSPLUS >= 201703L
|
||||||
#endif
|
|
||||||
|
|
||||||
TEST(base_test, has_formatter) {
|
TEST(base_test, has_formatter) {
|
||||||
EXPECT_TRUE((fmt::detail::has_formatter<const const_formattable, char>()));
|
EXPECT_TRUE((fmt::detail::has_formatter<const const_formattable, char>()));
|
||||||
@@ -868,3 +861,17 @@ TEST(base_test, format_to_custom_container) {
|
|||||||
auto c = custom_container();
|
auto c = custom_container();
|
||||||
fmt::format_to(std::back_inserter(c), "");
|
fmt::format_to(std::back_inserter(c), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct nondeterministic_format_string {
|
||||||
|
mutable int i = 0;
|
||||||
|
FMT_CONSTEXPR operator string_view() const {
|
||||||
|
return string_view("{}", i++ != 0 ? 2 : 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(base_test, no_repeated_format_string_conversions) {
|
||||||
|
#if !FMT_GCC_VERSION
|
||||||
|
char buf[10];
|
||||||
|
fmt::format_to(buf, nondeterministic_format_string());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "fmt/compile.h"
|
#include "fmt/compile.h"
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "fmt/chrono.h"
|
#include "fmt/chrono.h"
|
||||||
#include "fmt/ranges.h"
|
#include "fmt/ranges.h"
|
||||||
@@ -229,10 +230,14 @@ TEST(compile_test, unknown_format_fallback) {
|
|||||||
EXPECT_EQ(" 42 ",
|
EXPECT_EQ(" 42 ",
|
||||||
fmt::format(FMT_COMPILE("{name:^4}"), fmt::arg("name", 42)));
|
fmt::format(FMT_COMPILE("{name:^4}"), fmt::arg("name", 42)));
|
||||||
|
|
||||||
std::vector<char> v;
|
std::vector<char> v1;
|
||||||
fmt::format_to(std::back_inserter(v), FMT_COMPILE("{name:^4}"),
|
fmt::format_to(std::back_inserter(v1), FMT_COMPILE("{}"), 42);
|
||||||
|
EXPECT_EQ("42", fmt::string_view(v1.data(), v1.size()));
|
||||||
|
|
||||||
|
std::vector<char> v2;
|
||||||
|
fmt::format_to(std::back_inserter(v2), FMT_COMPILE("{name:^4}"),
|
||||||
fmt::arg("name", 42));
|
fmt::arg("name", 42));
|
||||||
EXPECT_EQ(" 42 ", fmt::string_view(v.data(), v.size()));
|
EXPECT_EQ(" 42 ", fmt::string_view(v2.data(), v2.size()));
|
||||||
|
|
||||||
char buffer[4];
|
char buffer[4];
|
||||||
auto result = fmt::format_to_n(buffer, 4, FMT_COMPILE("{name:^5}"),
|
auto result = fmt::format_to_n(buffer, 4, FMT_COMPILE("{name:^5}"),
|
||||||
@@ -265,11 +270,23 @@ TEST(compile_test, to_string_and_formatter) {
|
|||||||
fmt::format(FMT_COMPILE("{}"), to_stringable());
|
fmt::format(FMT_COMPILE("{}"), to_stringable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct std_context_test {};
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
template <> struct formatter<std_context_test> : formatter<int> {
|
||||||
|
auto format(std_context_test, format_context& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
TEST(compile_test, print) {
|
TEST(compile_test, print) {
|
||||||
EXPECT_WRITE(stdout, fmt::print(FMT_COMPILE("Don't {}!"), "panic"),
|
EXPECT_WRITE(stdout, fmt::print(FMT_COMPILE("Don't {}!"), "panic"),
|
||||||
"Don't panic!");
|
"Don't panic!");
|
||||||
EXPECT_WRITE(stderr, fmt::print(stderr, FMT_COMPILE("Don't {}!"), "panic"),
|
EXPECT_WRITE(stderr, fmt::print(stderr, FMT_COMPILE("Don't {}!"), "panic"),
|
||||||
"Don't panic!");
|
"Don't panic!");
|
||||||
|
fmt::print(FMT_COMPILE("{}"), std_context_test());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ struct double_double {
|
|||||||
double a;
|
double a;
|
||||||
double b;
|
double b;
|
||||||
|
|
||||||
explicit constexpr double_double(double a_val = 0, double b_val = 0)
|
constexpr explicit double_double(double a_val = 0, double b_val = 0)
|
||||||
: a(a_val), b(b_val) {}
|
: a(a_val), b(b_val) {}
|
||||||
|
|
||||||
operator double() const { return a + b; }
|
operator double() const { return a + b; }
|
||||||
@@ -299,7 +299,7 @@ bool operator>=(const double_double& lhs, const double_double& rhs) {
|
|||||||
struct slow_float {
|
struct slow_float {
|
||||||
float value;
|
float value;
|
||||||
|
|
||||||
explicit constexpr slow_float(float val = 0) : value(val) {}
|
constexpr explicit slow_float(float val = 0) : value(val) {}
|
||||||
operator float() const { return value; }
|
operator float() const { return value; }
|
||||||
auto operator-() const -> slow_float { return slow_float(-value); }
|
auto operator-() const -> slow_float { return slow_float(-value); }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1821,7 +1821,9 @@ TEST(format_test, big_print) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Windows CRT implements _IOLBF incorrectly (full buffering).
|
// Windows CRT implements _IOLBF incorrectly (full buffering).
|
||||||
#if FMT_USE_FCNTL && !defined(_WIN32)
|
#if FMT_USE_FCNTL
|
||||||
|
|
||||||
|
# ifndef _WIN32
|
||||||
TEST(format_test, line_buffering) {
|
TEST(format_test, line_buffering) {
|
||||||
auto pipe = fmt::pipe();
|
auto pipe = fmt::pipe();
|
||||||
|
|
||||||
@@ -1845,7 +1847,26 @@ TEST(format_test, line_buffering) {
|
|||||||
|
|
||||||
reader.join();
|
reader.join();
|
||||||
}
|
}
|
||||||
#endif
|
# endif
|
||||||
|
|
||||||
|
TEST(format_test, buffer_boundary) {
|
||||||
|
auto pipe = fmt::pipe();
|
||||||
|
|
||||||
|
auto write_end = pipe.write_end.fdopen("w");
|
||||||
|
setvbuf(write_end.get(), nullptr, _IOFBF, 4096);
|
||||||
|
for (int i = 3; i < 4094; i++)
|
||||||
|
write_end.print("{}", (i % 73) != 0 ? 'x' : '\n');
|
||||||
|
write_end.print("{} {}", 1234, 567);
|
||||||
|
write_end.close();
|
||||||
|
|
||||||
|
auto read_end = pipe.read_end.fdopen("r");
|
||||||
|
char buf[4091] = {};
|
||||||
|
size_t n = fread(buf, 1, sizeof(buf), read_end.get());
|
||||||
|
EXPECT_EQ(n, sizeof(buf));
|
||||||
|
EXPECT_STREQ(fgets(buf, sizeof(buf), read_end.get()), "1234 567");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
||||||
struct deadlockable {
|
struct deadlockable {
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
|||||||
24
test/no-builtin-types-test.cc
Normal file
24
test/no-builtin-types-test.cc
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Formatting library for C++ - formatting library tests
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#if !defined(__GNUC__) || __GNUC__ >= 5
|
||||||
|
#define FMT_BUILTIN_TYPES 0
|
||||||
|
#include "fmt/format.h"
|
||||||
|
|
||||||
|
TEST(no_builtin_types_test, format) {
|
||||||
|
EXPECT_EQ(fmt::format("{}", 42), "42");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(no_builtin_types_test, double_is_custom_type) {
|
||||||
|
double d = 42;
|
||||||
|
auto args = fmt::make_format_args(d);
|
||||||
|
EXPECT_EQ(fmt::format_args(args).get(0).type(),
|
||||||
|
fmt::detail::type::custom_type);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -28,11 +28,6 @@
|
|||||||
# define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
|
# define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1910
|
|
||||||
# define FMT_RANGES_TEST_ENABLE_JOIN
|
|
||||||
# define FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
|
#ifdef FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
|
||||||
TEST(ranges_test, format_array) {
|
TEST(ranges_test, format_array) {
|
||||||
int arr[] = {1, 2, 3, 5, 7, 11};
|
int arr[] = {1, 2, 3, 5, 7, 11};
|
||||||
@@ -213,7 +208,6 @@ TEST(ranges_test, tuple_parse_calls_element_parse) {
|
|||||||
EXPECT_THROW(f.parse(ctx), bad_format);
|
EXPECT_THROW(f.parse(ctx), bad_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
|
|
||||||
struct tuple_like {
|
struct tuple_like {
|
||||||
int i;
|
int i;
|
||||||
std::string str;
|
std::string str;
|
||||||
@@ -246,7 +240,6 @@ TEST(ranges_test, format_struct) {
|
|||||||
auto t = tuple_like{42, "foo"};
|
auto t = tuple_like{42, "foo"};
|
||||||
EXPECT_EQ(fmt::format("{}", t), "(42, \"foo\")");
|
EXPECT_EQ(fmt::format("{}", t), "(42, \"foo\")");
|
||||||
}
|
}
|
||||||
#endif // FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
|
|
||||||
|
|
||||||
TEST(ranges_test, format_to) {
|
TEST(ranges_test, format_to) {
|
||||||
char buf[10];
|
char buf[10];
|
||||||
@@ -401,7 +394,6 @@ TEST(ranges_test, join_bytes) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FMT_RANGES_TEST_ENABLE_JOIN
|
|
||||||
TEST(ranges_test, join_tuple) {
|
TEST(ranges_test, join_tuple) {
|
||||||
// Value tuple args.
|
// Value tuple args.
|
||||||
auto t1 = std::tuple<char, int, float>('a', 1, 2.0f);
|
auto t1 = std::tuple<char, int, float>('a', 1, 2.0f);
|
||||||
@@ -420,6 +412,10 @@ TEST(ranges_test, join_tuple) {
|
|||||||
auto t4 = std::tuple<float>(4.0f);
|
auto t4 = std::tuple<float>(4.0f);
|
||||||
EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4");
|
EXPECT_EQ(fmt::format("{}", fmt::join(t4, "/")), "4");
|
||||||
|
|
||||||
|
// Tuple-like.
|
||||||
|
auto t5 = tuple_like{42, "foo"};
|
||||||
|
EXPECT_EQ(fmt::format("{}", fmt::join(t5, ", ")), "42, foo");
|
||||||
|
|
||||||
# if FMT_TUPLE_JOIN_SPECIFIERS
|
# if FMT_TUPLE_JOIN_SPECIFIERS
|
||||||
// Specs applied to each element.
|
// Specs applied to each element.
|
||||||
auto t5 = std::tuple<int, int, long>(-3, 100, 1);
|
auto t5 = std::tuple<int, int, long>(-3, 100, 1);
|
||||||
@@ -533,8 +529,6 @@ TEST(ranges_test, format_join_adl_begin_end) {
|
|||||||
EXPECT_EQ(fmt::format("{}", fmt::join(adl::vec(), "/")), "42/43");
|
EXPECT_EQ(fmt::format("{}", fmt::join(adl::vec(), "/")), "42/43");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_RANGES_TEST_ENABLE_JOIN
|
|
||||||
|
|
||||||
#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 202207L
|
#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 202207L
|
||||||
TEST(ranges_test, nested_ranges) {
|
TEST(ranges_test, nested_ranges) {
|
||||||
auto l = std::list{1, 2, 3};
|
auto l = std::list{1, 2, 3};
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ class scan_parse_context {
|
|||||||
public:
|
public:
|
||||||
using iterator = string_view::iterator;
|
using iterator = string_view::iterator;
|
||||||
|
|
||||||
explicit FMT_CONSTEXPR scan_parse_context(string_view format)
|
FMT_CONSTEXPR explicit scan_parse_context(string_view format)
|
||||||
: format_(format) {}
|
: format_(format) {}
|
||||||
|
|
||||||
FMT_CONSTEXPR auto begin() const -> iterator { return format_.begin(); }
|
FMT_CONSTEXPR auto begin() const -> iterator { return format_.begin(); }
|
||||||
@@ -347,7 +347,7 @@ class scan_context {
|
|||||||
using iterator = detail::scan_iterator;
|
using iterator = detail::scan_iterator;
|
||||||
using sentinel = detail::scan_sentinel;
|
using sentinel = detail::scan_sentinel;
|
||||||
|
|
||||||
explicit FMT_CONSTEXPR scan_context(detail::scan_buffer& buf, scan_args args)
|
FMT_CONSTEXPR explicit scan_context(detail::scan_buffer& buf, scan_args args)
|
||||||
: buf_(buf), args_(args) {}
|
: buf_(buf), args_(args) {}
|
||||||
|
|
||||||
FMT_CONSTEXPR auto arg(int id) const -> scan_arg {
|
FMT_CONSTEXPR auto arg(int id) const -> scan_arg {
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ TEST(std_test, complex) {
|
|||||||
EXPECT_EQ(fmt::format("{: }", std::complex<double>(1, 2.2)), "( 1+2.2i)");
|
EXPECT_EQ(fmt::format("{: }", std::complex<double>(1, 2.2)), "( 1+2.2i)");
|
||||||
EXPECT_EQ(fmt::format("{: }", std::complex<double>(1, -2.2)), "( 1-2.2i)");
|
EXPECT_EQ(fmt::format("{: }", std::complex<double>(1, -2.2)), "( 1-2.2i)");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:8}", std::complex<double>(1, 2)), "(1+2i) ");
|
||||||
|
EXPECT_EQ(fmt::format("{:-<8}", std::complex<double>(1, 2)), "(1+2i)--");
|
||||||
|
|
||||||
EXPECT_EQ(fmt::format("{:>20.2f}", std::complex<double>(1, 2.2)),
|
EXPECT_EQ(fmt::format("{:>20.2f}", std::complex<double>(1, 2.2)),
|
||||||
" (1.00+2.20i)");
|
" (1.00+2.20i)");
|
||||||
EXPECT_EQ(fmt::format("{:<20.2f}", std::complex<double>(1, 2.2)),
|
EXPECT_EQ(fmt::format("{:<20.2f}", std::complex<double>(1, 2.2)),
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
void throw_assertion_failure(const char* message);
|
void throw_assertion_failure(const char* message);
|
||||||
#define FMT_ASSERT(condition, message) \
|
#define FMT_ASSERT(condition, message) \
|
||||||
if (!(condition)) throw_assertion_failure(message);
|
((condition) ? (void)0 : throw_assertion_failure(message))
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
|||||||
@@ -72,16 +72,17 @@ TEST(xchar_test, format_explicitly_convertible_to_wstring_view) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
TEST(xchar_test, format) {
|
TEST(xchar_test, format) {
|
||||||
EXPECT_EQ(L"42", fmt::format(L"{}", 42));
|
EXPECT_EQ(fmt::format(L"{}", 42), L"42");
|
||||||
EXPECT_EQ(L"4.2", fmt::format(L"{}", 4.2));
|
EXPECT_EQ(fmt::format(L"{}", 4.2), L"4.2");
|
||||||
EXPECT_EQ(L"abc", fmt::format(L"{}", L"abc"));
|
EXPECT_EQ(fmt::format(L"{}", L"abc"), L"abc");
|
||||||
EXPECT_EQ(L"z", fmt::format(L"{}", L'z'));
|
EXPECT_EQ(fmt::format(L"{}", L'z'), L"z");
|
||||||
EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error);
|
EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error);
|
||||||
EXPECT_EQ(L"true", fmt::format(L"{}", true));
|
EXPECT_EQ(fmt::format(L"{}", true), L"true");
|
||||||
EXPECT_EQ(L"a", fmt::format(L"{0}", L'a'));
|
EXPECT_EQ(fmt::format(L"{0}", L'a'), L"a");
|
||||||
EXPECT_EQ(L"Cyrillic letter \x42e",
|
EXPECT_EQ(fmt::format(L"Letter {}", L'\x40e'), L"Letter \x40e"); // Ў
|
||||||
fmt::format(L"Cyrillic letter {}", L'\x42e'));
|
if (sizeof(wchar_t) == 4)
|
||||||
EXPECT_EQ(L"abc1", fmt::format(L"{}c{}", L"ab", 1));
|
EXPECT_EQ(fmt::format(fmt::runtime(L"{:𓀨>3}"), 42), L"𓀨42");
|
||||||
|
EXPECT_EQ(fmt::format(L"{}c{}", L"ab", 1), L"abc1");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(xchar_test, is_formattable) {
|
TEST(xchar_test, is_formattable) {
|
||||||
@@ -490,12 +491,20 @@ TEST(locale_test, sign) {
|
|||||||
EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50");
|
EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(std_test_xchar, format_bitset) {
|
||||||
|
auto bs = std::bitset<6>(42);
|
||||||
|
EXPECT_EQ(fmt::format(L"{}", bs), L"101010");
|
||||||
|
EXPECT_EQ(fmt::format(L"{:0>8}", bs), L"00101010");
|
||||||
|
EXPECT_EQ(fmt::format(L"{:-^12}", bs), L"---101010---");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(std_test_xchar, complex) {
|
TEST(std_test_xchar, complex) {
|
||||||
auto s = fmt::format(L"{}", std::complex<double>(1, 2));
|
auto s = fmt::format(L"{}", std::complex<double>(1, 2));
|
||||||
EXPECT_EQ(s, L"(1+2i)");
|
EXPECT_EQ(s, L"(1+2i)");
|
||||||
EXPECT_EQ(fmt::format(L"{:.2f}", std::complex<double>(1, 2)),
|
EXPECT_EQ(fmt::format(L"{:.2f}", std::complex<double>(1, 2)),
|
||||||
L"(1.00+2.00i)");
|
L"(1.00+2.00i)");
|
||||||
EXPECT_EQ(fmt::format(L"{:8}", std::complex<double>(1, 2)), L"(1+2i) ");
|
EXPECT_EQ(fmt::format(L"{:8}", std::complex<double>(1, 2)), L"(1+2i) ");
|
||||||
|
EXPECT_EQ(fmt::format(L"{:-<8}", std::complex<double>(1, 2)), L"(1+2i)--");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(std_test_xchar, optional) {
|
TEST(std_test_xchar, optional) {
|
||||||
|
|||||||
Reference in New Issue
Block a user