Merge commit '5ff285a9ed91944b686cd1d77ff28bafa9975695' into dev

This commit is contained in:
Simone
2025-01-07 15:10:49 +00:00
41 changed files with 1068 additions and 774 deletions

View File

@@ -10,18 +10,20 @@ jobs:
steps:
- name: Build fuzzers
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@92182553173581f871130c71c71b17f003d47b0a # master
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@92182553173581f871130c71c71b17f003d47b0a
with:
oss-fuzz-project-name: 'fmt'
dry-run: false
language: c++
- 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:
oss-fuzz-project-name: 'fmt'
fuzz-seconds: 300
dry-run: false
language: c++
- name: Upload crash
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
if: failure() && steps.build.outcome == 'success'

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Add Ubuntu mirrors
run: |

View File

@@ -13,14 +13,16 @@ jobs:
format_code:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Install clang-format
uses: aminya/setup-cpp@290824452986e378826155f3379d31bce8753d76 # v0.37.0
with:
clangformat: 17.0.5
run: |
wget https://apt.llvm.org/llvm.sh
sudo bash ./llvm.sh 17
sudo apt install clang-format-17
- name: Run clang-format
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

View File

@@ -62,7 +62,7 @@ jobs:
shared: -DBUILD_SHARED_LIBS=ON
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set timezone
run: sudo timedatectl set-timezone 'Asia/Yekaterinburg'

View File

@@ -25,7 +25,7 @@ jobs:
runs-on: '${{ matrix.os }}'
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set timezone
run: sudo systemsetup -settimezone 'Asia/Yekaterinburg'

View File

@@ -29,7 +29,7 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
with:
persist-credentials: false
@@ -60,6 +60,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- 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:
sarif_file: results.sarif

View File

@@ -36,7 +36,7 @@ jobs:
standard: 20
steps:
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Set timezone
run: tzutil /s "Ekaterinburg Standard Time"
@@ -78,12 +78,12 @@ jobs:
- name: Set timezone
run: tzutil /s "Ekaterinburg Standard Time"
shell: cmd
- uses: msys2/setup-msys2@5df0ca6cbf14efcd08f8d5bd5e049a3cc8e07fd2 # v2.24.0
- uses: msys2/setup-msys2@c52d1fa9c7492275e60fe763540fb601f5f232a1 # v2.25.0
with:
release: false
msystem: ${{matrix.sys}}
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
run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug
env: { LDFLAGS: -fuse-ld=lld }

View File

@@ -426,7 +426,9 @@ if (FMT_INSTALL)
endif()
# 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}
ARCHIVE DESTINATION ${FMT_LIB_DIR}
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
@@ -439,13 +441,15 @@ if (FMT_INSTALL)
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
# Install version, config and target files.
install(
FILES ${project_config} ${version_config}
DESTINATION ${FMT_CMAKE_DIR})
install(FILES ${project_config} ${version_config}
DESTINATION ${FMT_CMAKE_DIR}
COMPONENT core)
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 ()
function(add_doc_target)
@@ -481,7 +485,8 @@ function(add_doc_target)
include(GNUInstallDirs)
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()
if (FMT_DOC)

View File

@@ -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
@@ -272,6 +503,9 @@
- Fixed handling of negative ids in `fmt::basic_format_args::get`
(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
(https://github.com/fmtlib/fmt/issues/3817).

View File

@@ -291,6 +291,7 @@ converts to `std::print`.)
- [ccache](https://ccache.dev/): a compiler cache
- [ClickHouse](https://github.com/ClickHouse/ClickHouse): an
analytical database management system
- [ContextVision](https://www.contextvision.com/): medical imaging software
- [Contour](https://github.com/contour-terminal/contour/): a modern
terminal emulator
- [CUAUV](https://cuauv.org/): Cornell University\'s autonomous

View File

@@ -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.
::: 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
::: runtime(string_view)
### Named Arguments
::: arg(const Char*, const T&)
Named arguments are not supported in compile-time checks at the moment.
### Type Erasure
You can create your own formatting function with compile-time checks and
@@ -317,6 +315,12 @@ parameterized version.
::: basic_format_arg
### Named Arguments
::: arg(const Char*, const T&)
Named arguments are not supported in compile-time checks at the moment.
### Compatibility
::: basic_string_view
@@ -375,18 +379,17 @@ allocator:
using custom_string =
std::basic_string<char, std::char_traits<char>, custom_allocator>;
custom_string vformat(custom_allocator alloc, fmt::string_view format_str,
fmt::format_args args) {
auto vformat(custom_allocator alloc, fmt::string_view fmt,
fmt::format_args args) -> custom_string {
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);
}
template <typename ...Args>
inline custom_string format(custom_allocator alloc,
fmt::string_view format_str,
const Args& ... args) {
return vformat(alloc, format_str, fmt::make_format_args(args...));
auto format(custom_allocator alloc, fmt::string_view fmt,
const Args& ... args) -> custom_string {
return vformat(alloc, fmt, fmt::make_format_args(args...));
}
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
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>
### Legacy Compile-Time Checks
@@ -498,10 +501,13 @@ chrono-format-specifications).
- [`std::atomic_flag`](https://en.cppreference.com/w/cpp/atomic/atomic_flag)
- [`std::bitset`](https://en.cppreference.com/w/cpp/utility/bitset)
- [`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::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::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::variant`](https://en.cppreference.com/w/cpp/utility/variant/variant)
@@ -509,7 +515,7 @@ chrono-format-specifications).
::: ptr(const std::shared_ptr<T>&)
### Formatting Variants
### Variants
A `std::variant` is only formattable if every variant alternative is
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>());
// 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>
## Format String Compilation
`fmt/compile.h` provides format string compilation enabled via the
`FMT_COMPILE` macro or the `_cf` user-defined literal defined in
namespace `fmt::literals`. Format strings marked with `FMT_COMPILE`
or `_cf` are parsed, checked and converted into efficient formatting
code at compile-time. This supports arguments of built-in and string
types as well as user-defined types with `format` functions taking
`fmt/compile.h` provides format string compilation and compile-time
(`constexpr`) formatting enabled via the `FMT_COMPILE` macro or the `_cf`
user-defined literal defined in namespace `fmt::literals`. Format strings
marked with `FMT_COMPILE` or `_cf` are parsed, checked and converted into
efficient formatting code at compile-time. This supports arguments of built-in
and string types as well as user-defined types with `format` functions taking
the format context type as a template parameter in their `formatter`
specializations. For example:

View File

@@ -202,7 +202,7 @@ For a static build, use the following subproject definition:
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')
### Android NDK

View File

@@ -122,8 +122,8 @@ hide:
</p>
<p>
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
(2015) and later. Newer compiler and standard library features are used
C++11</b> features which are available in GCC 4.9, Clang 3.4, MSVC 19.10
(2017) and later. Newer compiler and standard library features are used
if available, and enable additional functionality.
</p>
<p>

View File

@@ -706,12 +706,12 @@ The available padding modifiers (*padding_modifier*) are:
| Type | Meaning |
|-------|-----------------------------------------|
| `'-'` | Pad a numeric result with spaces. |
| `'_'` | Do not pad a numeric result string. |
| `'_'` | Pad a numeric result with spaces. |
| `'-'` | Do not pad a numeric result string. |
| `'0'` | Pad a numeric result string with zeros. |
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

View File

@@ -210,6 +210,9 @@ template <typename Context> class dynamic_format_arg_store {
data_.reserve(new_cap);
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

View File

@@ -21,7 +21,7 @@
#endif
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 110002
#define FMT_VERSION 110101
// Detect compiler versions.
#if defined(__clang__) && !defined(__ibmxl__)
@@ -146,6 +146,8 @@
// Use the provided definition.
#elif defined(__GNUC__) && !defined(__EXCEPTIONS)
# define FMT_USE_EXCEPTIONS 0
#elif defined(__clang__) && !defined(__cpp_exceptions)
# define FMT_USE_EXCEPTIONS 0
#elif FMT_MSC_VERSION && !_HAS_EXCEPTIONS
# define FMT_USE_EXCEPTIONS 0
#else
@@ -159,6 +161,20 @@
# define FMT_CATCH(x) if (false)
#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)
# define FMT_FALLTHROUGH [[fallthrough]]
#elif defined(__clang__)
@@ -332,6 +348,13 @@ struct monostate {
# define FMT_ENABLE_IF(...) fmt::enable_if_t<(__VA_ARGS__), int> = 0
#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 {
// Suppresses "unused variable" warnings with the method described in
// 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.
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,
const char* message);
@@ -394,7 +419,7 @@ inline auto map(uint128_opt) -> monostate { return {}; }
#endif
#ifndef FMT_USE_BITINT
# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1400)
# define FMT_USE_BITINT (FMT_CLANG_VERSION >= 1500)
#endif
#if FMT_USE_BITINT
@@ -562,8 +587,8 @@ template <typename Char> class basic_string_view {
// Lexicographically compare this string reference to other.
FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
size_t str_size = size_ < other.size_ ? size_ : other.size_;
int result = detail::compare(data_, other.data_, str_size);
int result =
detail::compare(data_, other.data_, min_of(size_, other.size_));
if (result != 0) return result;
return size_ == other.size_ ? 0 : (size_ < other.size_ ? -1 : 1);
}
@@ -714,7 +739,7 @@ class basic_specs {
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.
char fill_data_[max_fill_size] = {' '};
@@ -793,7 +818,8 @@ class basic_specs {
template <typename Char> constexpr auto fill_unit() const -> Char {
using uchar = unsigned char;
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) {
@@ -809,12 +835,19 @@ class basic_specs {
unsigned uchar = static_cast<detail::unsigned_char<Char>>(s[0]);
fill_data_[0] = static_cast<char>(uchar);
fill_data_[1] = static_cast<char>(uchar >> 8);
fill_data_[2] = static_cast<char>(uchar >> 16);
return;
}
FMT_ASSERT(size <= max_fill_size, "invalid fill");
for (size_t i = 0; i < size; ++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.
@@ -842,7 +875,7 @@ template <typename Char = char> class parse_context {
using char_type = 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)
: 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");
};
template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
template <bool B = false> constexpr auto count() -> int { return B ? 1 : 0; }
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int {
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...>();
}
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...>();
}
@@ -1180,7 +1213,7 @@ class compile_parse_context : public parse_context<Char> {
using base = parse_context<Char>;
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 next_arg_id = 0)
: 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>
class format_string_checker {
private:
type types_[NUM_ARGS > 0 ? NUM_ARGS : 1];
named_arg_info<Char> named_args_[NUM_NAMED_ARGS > 0 ? NUM_NAMED_ARGS : 1];
type types_[max_of(1, NUM_ARGS)];
named_arg_info<Char> named_args_[max_of(1, NUM_NAMED_ARGS)];
compile_parse_context<Char> context_;
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:
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...>)
: types_{mapped_type_constant<T, Char>::value...},
named_args_{},
@@ -1694,7 +1727,7 @@ template <typename T> class buffer {
protected:
// Don't initialize ptr_ since it is not accessed to save a few cycles.
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) {}
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.
FMT_CONSTEXPR void try_resize(size_t 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
@@ -1788,9 +1821,9 @@ template <typename T> class buffer {
};
struct buffer_traits {
explicit buffer_traits(size_t) {}
auto count() const -> size_t { return 0; }
auto limit(size_t size) -> size_t { return size; }
constexpr explicit buffer_traits(size_t) {}
constexpr auto count() const -> size_t { return 0; }
constexpr auto limit(size_t size) const -> size_t { return size; }
};
class fixed_buffer_traits {
@@ -1799,12 +1832,12 @@ class fixed_buffer_traits {
size_t limit_;
public:
explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
auto count() const -> size_t { return count_; }
auto limit(size_t size) -> size_t {
constexpr explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
constexpr auto count() const -> size_t { return count_; }
FMT_CONSTEXPR auto limit(size_t size) -> size_t {
size_t n = limit_ > count_ ? limit_ - count_ : 0;
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>
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).
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)
-> OutputIt {
get_container(out).append(begin, end);
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,
FMT_ENABLE_IF(!is_back_insert_iterator<OutputIt>::value)>
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)>
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_ALWAYS_INLINE value(const named_arg_info<char_type>* args, size_t size)
@@ -2220,9 +2276,12 @@ struct locale_ref {
public:
constexpr locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc);
explicit operator bool() const noexcept { return locale_ != nullptr; }
#endif
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
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;
};
@@ -2243,16 +2302,15 @@ constexpr auto make_descriptor() -> unsigned long long {
: 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>,
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>
struct named_arg_store {
// 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 != 0 ? NUM_ARGS : +1)];
arg_t<Context, NUM_ARGS> args[1 + NUM_ARGS];
named_arg_info<typename Context::char_type> named_args[NUM_NAMED_ARGS];
template <typename... T>
@@ -2280,13 +2338,13 @@ struct named_arg_store {
// An array of references to arguments. It can be implicitly converted to
// `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.
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>
struct format_arg_store {
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
using type =
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>>;
type args;
};
@@ -2372,11 +2430,6 @@ template <typename T> class basic_appender {
detail::buffer<T>* container;
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>;
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);
}
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 =
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) {}
/// 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)>
constexpr FMT_ALWAYS_INLINE basic_format_args(
const store<NUM_ARGS, NUM_NAMED_ARGS, DESC>& s)
: desc_(DESC | (NUM_NAMED_ARGS != 0 ? +detail::has_named_args_bit : 0)),
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)>
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)),
@@ -2571,10 +2624,11 @@ template <typename Context> class basic_format_args {
};
// A formatting context.
class context : private detail::locale_ref {
class context {
private:
appender out_;
format_args args_;
FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_;
public:
/// 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.
FMT_CONSTEXPR context(iterator out, format_args args,
detail::locale_ref loc = {})
: locale_ref(loc), out_(out), args_(args) {}
: out_(out), args_(args), loc_(loc) {}
context(context&&) = default;
context(const context&) = delete;
void operator=(const context&) = delete;
FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); }
auto arg(string_view name) -> format_arg { return args_.get(name); }
FMT_CONSTEXPR auto arg_id(string_view name) -> int {
inline auto arg(string_view name) const -> format_arg {
return args_.get(name);
}
FMT_CONSTEXPR auto arg_id(string_view name) const -> int {
return args_.get_id(name);
}
// 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`.
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 {
@@ -2624,7 +2680,8 @@ template <typename Char = char> struct runtime_format_string {
*/
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 {
private:
static constexpr int num_static_named_args =
@@ -2657,8 +2714,9 @@ template <typename... T> struct fstring {
template <typename S,
FMT_ENABLE_IF(std::is_convertible<const S&, string_view>::value)>
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const S& s) : str(s) {
auto sv = string_view(str);
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
static_assert(
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.
// auto args = make_format_args(std::string());
template <typename Context = context, typename... T,
size_t NUM_ARGS = sizeof...(T),
size_t NUM_NAMED_ARGS = detail::count_named_args<T...>(),
int NUM_ARGS = sizeof...(T),
int NUM_NAMED_ARGS = detail::count_named_args<T...>(),
unsigned long long DESC = detail::make_descriptor<Context, T...>()>
constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args)
-> 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>
FMT_INLINE void print(format_string<T...> fmt, T&&... 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::is_locking<T...>() ? vprint_buffered(stdout, fmt.str, va)
: vprint(fmt.str, va);
@@ -2868,7 +2926,8 @@ FMT_INLINE void print(format_string<T...> fmt, T&&... args) {
template <typename... T>
FMT_INLINE void print(FILE* f, format_string<T...> fmt, T&&... 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)
: vprint(f, fmt.str, va);
}
@@ -2878,7 +2937,8 @@ FMT_INLINE void print(FILE* f, format_string<T...> fmt, T&&... args) {
template <typename... T>
FMT_INLINE void println(FILE* f, format_string<T...> fmt, T&&... args) {
vargs<T...> va = {{args...}};
return detail::use_utf8 ? vprintln(f, fmt.str, va)
return detail::const_check(detail::use_utf8)
? vprintln(f, fmt.str, va)
: detail::vprint_mojibake(f, fmt.str, va, true);
}

View File

@@ -444,7 +444,7 @@ struct is_same_arithmetic_type
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"));
}
@@ -540,24 +540,24 @@ inline auto localtime(std::time_t time) -> std::tm {
std::time_t time_;
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;
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;
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
auto fallback(detail::null<>) -> bool {
inline auto fallback(detail::null<>) -> bool {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
@@ -591,24 +591,24 @@ inline auto gmtime(std::time_t time) -> std::tm {
std::time_t time_;
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;
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;
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
auto fallback(detail::null<>) -> bool {
inline auto fallback(detail::null<>) -> bool {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
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> {
FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); }
FMT_NORETURN inline void unsupported() {
FMT_THROW(format_error("no format"));
}
template <typename 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) {
*out++ = '.';
leading_zeroes = (std::min)(leading_zeroes, precision);
leading_zeroes = min_of(leading_zeroes, precision);
int remaining = precision - leading_zeroes;
out = detail::fill_n(out, leading_zeroes, '0');
if (remaining < num_digits) {
@@ -1572,7 +1574,7 @@ class tm_writer {
struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
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>
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
@@ -1693,14 +1695,14 @@ class get_locale {
bool has_locale_ = false;
public:
get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
inline get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
if (localized)
::new (&locale_) std::locale(loc.template get<std::locale>());
}
~get_locale() {
inline ~get_locale() {
if (has_locale_) locale_.~locale();
}
operator const std::locale&() const {
inline operator const std::locale&() const {
return has_locale_ ? locale_ : get_classic_locale();
}
};

View File

@@ -42,11 +42,10 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
#endif
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str>
template <typename Char, size_t N, fmt::detail::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string {
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};
}
};
@@ -525,9 +524,9 @@ FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& fmt, const Args&... args) {
memory_buffer buffer;
fmt::format_to(std::back_inserter(buffer), fmt, args...);
detail::print(f, {buffer.data(), buffer.size()});
auto buf = memory_buffer();
fmt::format_to(appender(buf), fmt, args...);
detail::print(f, {buf.data(), buf.size()});
}
template <typename S, typename... Args,
@@ -538,7 +537,7 @@ void print(const S& fmt, const Args&... args) {
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
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])>;
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
Str>();

View File

@@ -26,6 +26,10 @@
# include <locale>
#endif
#ifndef FMT_FUNC
# define FMT_FUNC
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
@@ -59,7 +63,7 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
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 {
memory_buffer full_message;
func(full_message, error_code, message);
@@ -80,7 +84,7 @@ using std::locale;
using std::numpunct;
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) {
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) {
#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
fputs(message, stderr);
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,
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 {

File diff suppressed because it is too large Load Diff

View File

@@ -176,24 +176,24 @@ class buffered_file {
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
inline explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// 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.
FMT_API ~buffered_file() noexcept;
public:
buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr;
}
auto operator=(buffered_file&& other) -> buffered_file& {
inline auto operator=(buffered_file&& other) -> buffered_file& {
close();
file_ = other.file_;
other.file_ = nullptr;
@@ -207,7 +207,7 @@ class buffered_file {
FMT_API void close();
// 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;
@@ -248,7 +248,7 @@ class FMT_API 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.
file(cstring_view path, int oflag);
@@ -257,10 +257,10 @@ class FMT_API file {
file(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.
auto operator=(file&& other) -> file& {
inline auto operator=(file&& other) -> file& {
close();
fd_ = other.fd_;
other.fd_ = -1;
@@ -271,7 +271,7 @@ class FMT_API file {
~file() noexcept;
// Returns the file descriptor.
auto descriptor() const noexcept -> int { return fd_; }
inline auto descriptor() const noexcept -> int { return fd_; }
// Closes the file.
void close();
@@ -324,9 +324,9 @@ auto getpagesize() -> long;
namespace detail {
struct buffer_size {
buffer_size() = default;
constexpr buffer_size() = default;
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();
bs.value = val;
return bs;
@@ -337,7 +337,7 @@ struct ostream_params {
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {}
constexpr ostream_params() {}
template <typename... T>
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
@@ -381,7 +381,7 @@ class FMT_API ostream : private detail::buffer<char> {
return buf;
}
void flush() {
inline void flush() {
if (size() == 0) return;
file_.write(data(), size() * sizeof(data()[0]));
clear();
@@ -390,7 +390,7 @@ class FMT_API ostream : private detail::buffer<char> {
template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream;
void close() {
inline void close() {
flush();
file_.close();
}

View File

@@ -22,6 +22,14 @@
#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
namespace detail {
@@ -35,7 +43,7 @@ class file_access {
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,
&std::filebuf::_Myfile>;
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();
detail::vformat_to(buffer, fmt, args);
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()))
f = detail::get_file(*buf);
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI

View File

@@ -79,7 +79,7 @@ template <bool IsSigned> struct int_checker {
unsigned max = to_unsigned(max_value<int>());
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> {
@@ -87,7 +87,7 @@ template <> struct int_checker<true> {
return value >= (std::numeric_limits<int>::min)() &&
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 {
@@ -205,7 +205,7 @@ class printf_width_handler {
format_specs& specs_;
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)>
auto operator()(T value) -> unsigned {

View File

@@ -357,12 +357,9 @@ template <typename R>
using maybe_const_range =
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>
struct is_formattable_delayed
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
#endif
} // namespace detail
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::map &&
range_format_kind<R, Char>::value != range_format::string &&
range_format_kind<R, Char>::value != range_format::debug_string>
// Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
,
detail::is_formattable_delayed<R, Char>
#endif
>::value>> {
range_format_kind<R, Char>::value != range_format::debug_string>,
detail::is_formattable_delayed<R, Char>>::value>> {
private:
using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
@@ -646,9 +638,9 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
#endif
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
using view_ref = conditional_t<std::is_copy_constructible<It>::value,
const join_view<It, Sentinel, Char>&,
join_view<It, Sentinel, Char>&&>;
using view = conditional_t<std::is_copy_constructible<It>::value,
const join_view<It, Sentinel, Char>,
join_view<It, Sentinel, Char>>;
public:
using nonlocking = void;
@@ -658,9 +650,10 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}
template <typename FormatContext>
auto format(view_ref& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto it = std::forward<view_ref>(value).begin;
auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
using iter =
conditional_t<std::is_copy_constructible<view>::value, It, It&>;
iter it = value.begin;
auto out = ctx.out();
if (it == value.end) return out;
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
/// 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>
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;
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
const Tuple& tuple;
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} {}
};
@@ -718,21 +683,22 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view {
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> {
template <typename Char, typename Tuple>
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* {
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
return do_parse(ctx, std::tuple_size<Tuple>());
}
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 {
return do_format(value, ctx,
std::integral_constant<size_t, sizeof...(T)>());
return do_format(value, ctx, std::tuple_size<Tuple>());
}
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,
std::integral_constant<size_t, 0>)
@@ -746,7 +712,7 @@ struct formatter<tuple_join_view<Char, T...>, Char> {
-> const Char* {
auto end = ctx.begin();
#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) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
@@ -757,18 +723,20 @@ struct formatter<tuple_join_view<Char, T...>, Char> {
}
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 ->
typename FormatContext::iterator {
return ctx.out();
}
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 ->
typename FormatContext::iterator {
auto out = std::get<sizeof...(T) - N>(formatters_)
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
using std::get;
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;
out = detail::copy<Char>(value.sep, out);
ctx.advance_to(out);
@@ -816,6 +784,34 @@ struct formatter<
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`.
*
@@ -825,9 +821,9 @@ FMT_BEGIN_EXPORT
* fmt::print("{}", fmt::join(t, ", "));
* // Output: 1, a
*/
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
-> tuple_join_view<char, T...> {
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<char, Tuple> {
return {tuple, sep};
}

View File

@@ -27,7 +27,8 @@
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# 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>
# endif
# if FMT_HAS_INCLUDE(<variant>)
@@ -183,7 +184,8 @@ FMT_END_NAMESPACE
FMT_BEGIN_NAMESPACE
FMT_EXPORT
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:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
@@ -203,7 +205,7 @@ struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> 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>
FMT_CONSTEXPR20 auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
FMT_CONSTEXPR20 auto format(const std::error_code& ec,
FormatContext& ctx) const -> decltype(ctx.out()) {
auto specs = specs_;
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx);
@@ -694,9 +696,7 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
auto outer_specs = format_specs();
outer_specs.width = specs.width;
auto fill = specs.template fill<Char>();
if (fill)
outer_specs.set_fill(basic_string_view<Char>(fill, specs.fill_size()));
outer_specs.set_fill(specs);
outer_specs.set_align(specs.align());
specs.width = 0;

View File

@@ -140,7 +140,7 @@ auto join(It begin, Sentinel end, wstring_view 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)
-> join_view<decltype(std::begin(range)), decltype(std::end(range)),
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);
}
template <typename... T>
auto join(const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, Tuple> {
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...));
}
template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(const Locale& loc, const S& fmt,
template <typename S, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto vformat(detail::locale_ref loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> std::basic_string<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()};
}
template <typename Locale, typename S, typename... T,
template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& fmt, T&&... args)
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto format(detail::locale_ref loc, const S& fmt, T&&... args)
-> std::basic_string<Char> {
return vformat(loc, detail::to_string_view(fmt),
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...));
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
template <typename S, typename OutputIt, typename... Args,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
inline auto vformat_to(OutputIt out, detail::locale_ref loc, const S& fmt,
typename detail::vformat_args<Char>::type args)
-> OutputIt {
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);
}
template <typename OutputIt, typename Locale, typename S, typename... T,
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::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) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(fmt),

View File

@@ -16,6 +16,7 @@ template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>;
#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 auto locale_ref::get<std::locale>() const -> std::locale;
#endif

View File

@@ -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 {
report_error(detail::format_windows_error, error_code, message);
do_report_error(detail::format_windows_error, error_code, message);
}
#endif // _WIN32

View File

@@ -1,10 +1,9 @@
#!/usr/bin/env python3
"""Manage site and releases.
"""Make a release.
Usage:
manage.py release [<branch>]
manage.py site
release.py [<branch>]
For the release command $FMT_TOKEN should contain a GitHub personal access token
obtained from https://github.com/settings/tokens.
@@ -12,9 +11,9 @@ obtained from https://github.com/settings/tokens.
from __future__ import print_function
import datetime, docopt, errno, fileinput, json, os
import re, requests, shutil, sys
from contextlib import contextmanager
import re, shutil, sys
from subprocess import check_call
import urllib.request
class Git:
@@ -81,46 +80,15 @@ def create_build_env():
return env
fmt_repo_url = 'git@github.com:fmtlib/fmt'
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):
if __name__ == '__main__':
args = docopt.docopt(__doc__)
env = create_build_env()
fmt_repo = env.fmt_repo
branch = args.get('<branch>')
if branch is None:
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)
# 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.
fmt_repo.push('origin', 'release')
auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')}
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
headers=auth_headers,
req = urllib.request.Request(
'https://api.github.com/repos/fmtlib/fmt/releases',
data=json.dumps({'tag_name': version,
'target_commitish': 'release',
'body': changes, 'draft': True}))
if r.status_code != 201:
raise Exception('Failed to create a release ' + str(r))
id = r.json()['id']
'body': changes, 'draft': True}).encode('utf-8'),
headers=auth_headers, method='POST')
with urllib.request.urlopen(req) as response:
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'
package = 'fmt-{}.zip'.format(version)
r = requests.post(
'{}/{}/assets?name={}'.format(uploads_url, id, package),
req = urllib.request.Request(
f'{uploads_url}/{id}/assets?name={package}',
headers={'Content-Type': 'application/zip'} | auth_headers,
data=open('build/fmt/' + package, 'rb'))
if r.status_code != 201:
raise Exception('Failed to upload an asset ' + str(r))
data=open('build/fmt/' + package, 'rb').read(), method='POST')
with urllib.request.urlopen(req) as response:
if response.status != 201:
raise Exception(f'Failed to upload an asset '
'{response.status} {response.reason}')
update_site(env)
if __name__ == '__main__':
args = docopt.docopt(__doc__)
if args.get('release'):
release(args)
elif args.get('site'):
update_site(create_build_env())
short_version = '.'.join(version.split('.')[:-1])
check_call(['./mkdocs', 'deploy', short_version])

View File

@@ -62,13 +62,14 @@ if (NOT (MSVC AND BUILD_SHARED_LIBS))
endif ()
add_fmt_test(ostream-test)
add_fmt_test(compile-test)
add_fmt_test(compile-fp-test HEADER_ONLY)
add_fmt_test(compile-fp-test)
if (MSVC)
# Without this option, MSVC returns 199711L for the __cplusplus macro.
target_compile_options(compile-fp-test PRIVATE /Zc:__cplusplus)
endif()
add_fmt_test(printf-test)
add_fmt_test(ranges-test ranges-odr-test.cc)
add_fmt_test(no-builtin-types-test HEADER_ONLY)
add_fmt_test(scan-test HEADER_ONLY)
check_symbol_exists(strptime "time.h" HAVE_STRPTIME)

View File

@@ -186,3 +186,17 @@ TEST(args_test, move_constructor) {
store.reset();
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);
}

View File

@@ -645,9 +645,7 @@ TEST(base_test, is_formattable) {
EXPECT_TRUE(fmt::is_formattable<const const_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);
#endif
EXPECT_FALSE(fmt::is_formattable<convertible_to_pointer>::value);
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);
}
#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 {
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, "");
}
# 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 {
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,
"");
}
# endif
#endif
#endif // FMT_CPLUSPLUS >= 201703L
TEST(base_test, has_formatter) {
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();
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
}

View File

@@ -8,6 +8,7 @@
#include "fmt/compile.h"
#include <type_traits>
#include <vector>
#include "fmt/chrono.h"
#include "fmt/ranges.h"
@@ -229,10 +230,14 @@ TEST(compile_test, unknown_format_fallback) {
EXPECT_EQ(" 42 ",
fmt::format(FMT_COMPILE("{name:^4}"), fmt::arg("name", 42)));
std::vector<char> v;
fmt::format_to(std::back_inserter(v), FMT_COMPILE("{name:^4}"),
std::vector<char> v1;
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));
EXPECT_EQ(" 42 ", fmt::string_view(v.data(), v.size()));
EXPECT_EQ(" 42 ", fmt::string_view(v2.data(), v2.size()));
char buffer[4];
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());
}
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) {
EXPECT_WRITE(stdout, fmt::print(FMT_COMPILE("Don't {}!"), "panic"),
"Don't panic!");
EXPECT_WRITE(stderr, fmt::print(stderr, FMT_COMPILE("Don't {}!"), "panic"),
"Don't panic!");
fmt::print(FMT_COMPILE("{}"), std_context_test());
}
#endif

View File

@@ -283,7 +283,7 @@ struct double_double {
double a;
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) {}
operator double() const { return a + b; }
@@ -299,7 +299,7 @@ bool operator>=(const double_double& lhs, const double_double& rhs) {
struct slow_float {
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; }
auto operator-() const -> slow_float { return slow_float(-value); }
};

View File

@@ -1821,7 +1821,9 @@ TEST(format_test, big_print) {
}
// Windows CRT implements _IOLBF incorrectly (full buffering).
#if FMT_USE_FCNTL && !defined(_WIN32)
#if FMT_USE_FCNTL
# ifndef _WIN32
TEST(format_test, line_buffering) {
auto pipe = fmt::pipe();
@@ -1847,6 +1849,25 @@ TEST(format_test, line_buffering) {
}
# 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 {
int value = 0;
mutable std::mutex mutex;

View 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

View File

@@ -28,11 +28,6 @@
# define FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
#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
TEST(ranges_test, format_array) {
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);
}
#ifdef FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
struct tuple_like {
int i;
std::string str;
@@ -246,7 +240,6 @@ TEST(ranges_test, format_struct) {
auto t = tuple_like{42, "foo"};
EXPECT_EQ(fmt::format("{}", t), "(42, \"foo\")");
}
#endif // FMT_RANGES_TEST_ENABLE_FORMAT_STRUCT
TEST(ranges_test, format_to) {
char buf[10];
@@ -401,7 +394,6 @@ TEST(ranges_test, join_bytes) {
}
#endif
#ifdef FMT_RANGES_TEST_ENABLE_JOIN
TEST(ranges_test, join_tuple) {
// Value tuple args.
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);
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
// Specs applied to each element.
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");
}
#endif // FMT_RANGES_TEST_ENABLE_JOIN
#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 202207L
TEST(ranges_test, nested_ranges) {
auto l = std::list{1, 2, 3};

View File

@@ -211,7 +211,7 @@ class scan_parse_context {
public:
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) {}
FMT_CONSTEXPR auto begin() const -> iterator { return format_.begin(); }
@@ -347,7 +347,7 @@ class scan_context {
using iterator = detail::scan_iterator;
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) {}
FMT_CONSTEXPR auto arg(int id) const -> scan_arg {

View File

@@ -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("{: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)),
" (1.00+2.20i)");
EXPECT_EQ(fmt::format("{:<20.2f}", std::complex<double>(1, 2.2)),

View File

@@ -12,7 +12,7 @@
void throw_assertion_failure(const char* message);
#define FMT_ASSERT(condition, message) \
if (!(condition)) throw_assertion_failure(message);
((condition) ? (void)0 : throw_assertion_failure(message))
#include "gtest/gtest.h"

View File

@@ -72,16 +72,17 @@ TEST(xchar_test, format_explicitly_convertible_to_wstring_view) {
#endif
TEST(xchar_test, format) {
EXPECT_EQ(L"42", fmt::format(L"{}", 42));
EXPECT_EQ(L"4.2", fmt::format(L"{}", 4.2));
EXPECT_EQ(L"abc", fmt::format(L"{}", L"abc"));
EXPECT_EQ(L"z", fmt::format(L"{}", L'z'));
EXPECT_EQ(fmt::format(L"{}", 42), L"42");
EXPECT_EQ(fmt::format(L"{}", 4.2), L"4.2");
EXPECT_EQ(fmt::format(L"{}", L"abc"), L"abc");
EXPECT_EQ(fmt::format(L"{}", L'z'), L"z");
EXPECT_THROW(fmt::format(fmt::runtime(L"{:*\x343E}"), 42), fmt::format_error);
EXPECT_EQ(L"true", fmt::format(L"{}", true));
EXPECT_EQ(L"a", fmt::format(L"{0}", L'a'));
EXPECT_EQ(L"Cyrillic letter \x42e",
fmt::format(L"Cyrillic letter {}", L'\x42e'));
EXPECT_EQ(L"abc1", fmt::format(L"{}c{}", L"ab", 1));
EXPECT_EQ(fmt::format(L"{}", true), L"true");
EXPECT_EQ(fmt::format(L"{0}", L'a'), L"a");
EXPECT_EQ(fmt::format(L"Letter {}", L'\x40e'), L"Letter \x40e"); // Ў
if (sizeof(wchar_t) == 4)
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) {
@@ -490,12 +491,20 @@ TEST(locale_test, sign) {
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) {
auto s = fmt::format(L"{}", std::complex<double>(1, 2));
EXPECT_EQ(s, L"(1+2i)");
EXPECT_EQ(fmt::format(L"{:.2f}", std::complex<double>(1, 2)),
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)--");
}
TEST(std_test_xchar, optional) {