Update {fmt} to 9.1.0
This commit is contained in:
parent
9c68d4792f
commit
99dea1a9d7
|
@ -1,3 +1,129 @@
|
||||||
|
9.1.0 - 2022-08-27
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* ``fmt::formatted_size`` now works at compile time
|
||||||
|
(`#3026 <https://github.com/fmtlib/fmt/pull/3026>`_). For example
|
||||||
|
(`godbolt <https://godbolt.org/z/1MW5rMdf8>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/compile.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
using namespace fmt::literals;
|
||||||
|
constexpr size_t n = fmt::formatted_size("{}"_cf, 42);
|
||||||
|
fmt::print("{}\n", n); // prints 2
|
||||||
|
}
|
||||||
|
|
||||||
|
Thanks `@marksantaniello (Mark Santaniello)
|
||||||
|
<https://github.com/marksantaniello>`_.
|
||||||
|
|
||||||
|
* Fixed handling of invalid UTF-8
|
||||||
|
(`#3038 <https://github.com/fmtlib/fmt/pull/3038>`_,
|
||||||
|
`#3044 <https://github.com/fmtlib/fmt/pull/3044>`_,
|
||||||
|
`#3056 <https://github.com/fmtlib/fmt/pull/3056>`_).
|
||||||
|
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_ and
|
||||||
|
`@skeeto (Christopher Wellons) <https://github.com/skeeto>`_.
|
||||||
|
|
||||||
|
* Improved Unicode support in ``ostream`` overloads of ``print``
|
||||||
|
(`#2994 <https://github.com/fmtlib/fmt/pull/2994>`_,
|
||||||
|
`#3001 <https://github.com/fmtlib/fmt/pull/3001>`_,
|
||||||
|
`#3025 <https://github.com/fmtlib/fmt/pull/3025>`_).
|
||||||
|
Thanks `@dimztimz (Dimitrij Mijoski) <https://github.com/dimztimz>`_.
|
||||||
|
|
||||||
|
* Fixed handling of the sign specifier in localized formatting on systems with
|
||||||
|
32-bit ``wchar_t`` (`#3041 <https://github.com/fmtlib/fmt/issues/3041>`_).
|
||||||
|
|
||||||
|
* Added support for wide streams to ``fmt::streamed``
|
||||||
|
(`#2994 <https://github.com/fmtlib/fmt/pull/2994>`_).
|
||||||
|
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
|
||||||
|
|
||||||
|
* Added the ``n`` specifier that disables the output of delimiters when
|
||||||
|
formatting ranges (`#2981 <https://github.com/fmtlib/fmt/pull/2981>`_,
|
||||||
|
`#2983 <https://github.com/fmtlib/fmt/pull/2983>`_).
|
||||||
|
For example (`godbolt <https://godbolt.org/z/roKqGdj8c>`__):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include <fmt/ranges.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto v = std::vector{1, 2, 3};
|
||||||
|
fmt::print("{:n}\n", v); // prints 1, 2, 3
|
||||||
|
}
|
||||||
|
|
||||||
|
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
|
||||||
|
|
||||||
|
* Worked around problematic ``std::string_view`` constructors introduced in
|
||||||
|
C++23 (`#3030 <https://github.com/fmtlib/fmt/issues/3030>`_,
|
||||||
|
`#3050 <https://github.com/fmtlib/fmt/issues/3050>`_).
|
||||||
|
Thanks `@strega-nil-ms (nicole mazzuca) <https://github.com/strega-nil-ms>`_.
|
||||||
|
|
||||||
|
* Improve handling (exclusion) of recursive ranges
|
||||||
|
(`#2968 <https://github.com/fmtlib/fmt/issues/2968>`_,
|
||||||
|
`#2974 <https://github.com/fmtlib/fmt/pull/2974>`_).
|
||||||
|
Thanks `@Dani-Hub (Daniel Krügler) <https://github.com/Dani-Hub>`_.
|
||||||
|
|
||||||
|
* Improved error reporting in format string compilation
|
||||||
|
(`#3055 <https://github.com/fmtlib/fmt/issues/3055>`_).
|
||||||
|
|
||||||
|
* Improved the implementation of
|
||||||
|
`Dragonbox <https://github.com/jk-jeon/dragonbox>`_, the algorithm used for
|
||||||
|
the default floating-point formatting
|
||||||
|
(`#2984 <https://github.com/fmtlib/fmt/pull/2984>`_).
|
||||||
|
Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_.
|
||||||
|
|
||||||
|
* Fixed issues with floating-point formatting on exotic platforms.
|
||||||
|
|
||||||
|
* Improved the implementation of chrono formatting
|
||||||
|
(`#3010 <https://github.com/fmtlib/fmt/pull/3010>`_).
|
||||||
|
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
|
||||||
|
|
||||||
|
* Improved documentation
|
||||||
|
(`#2966 <https://github.com/fmtlib/fmt/pull/2966>`_,
|
||||||
|
`#3009 <https://github.com/fmtlib/fmt/pull/3009>`_,
|
||||||
|
`#3020 <https://github.com/fmtlib/fmt/issues/3020>`_,
|
||||||
|
`#3037 <https://github.com/fmtlib/fmt/pull/3037>`_).
|
||||||
|
Thanks `@mwinterb <https://github.com/mwinterb>`_,
|
||||||
|
`@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_
|
||||||
|
and `@remiburtin (Rémi Burtin) <https://github.com/remiburtin>`_.
|
||||||
|
|
||||||
|
* Improved build configuration
|
||||||
|
(`#2991 <https://github.com/fmtlib/fmt/pull/2991>`_,
|
||||||
|
`#2995 <https://github.com/fmtlib/fmt/pull/2995>`_,
|
||||||
|
`#3004 <https://github.com/fmtlib/fmt/issues/3004>`_,
|
||||||
|
`#3007 <https://github.com/fmtlib/fmt/pull/3007>`_,
|
||||||
|
`#3040 <https://github.com/fmtlib/fmt/pull/3040>`_).
|
||||||
|
Thanks `@dimztimz (Dimitrij Mijoski) <https://github.com/dimztimz>`_ and
|
||||||
|
`@hwhsu1231 (Haowei Hsu) <https://github.com/hwhsu1231>`_.
|
||||||
|
|
||||||
|
* Fixed various warnings and compilation issues
|
||||||
|
(`#2969 <https://github.com/fmtlib/fmt/issues/2969>`_,
|
||||||
|
`#2971 <https://github.com/fmtlib/fmt/pull/2971>`_,
|
||||||
|
`#2975 <https://github.com/fmtlib/fmt/issues/2975>`_,
|
||||||
|
`#2982 <https://github.com/fmtlib/fmt/pull/2982>`_,
|
||||||
|
`#2985 <https://github.com/fmtlib/fmt/pull/2985>`_,
|
||||||
|
`#2988 <https://github.com/fmtlib/fmt/issues/2988>`_,
|
||||||
|
`#3000 <https://github.com/fmtlib/fmt/issues/3000>`_,
|
||||||
|
`#3006 <https://github.com/fmtlib/fmt/issues/3006>`_,
|
||||||
|
`#3014 <https://github.com/fmtlib/fmt/issues/3014>`_,
|
||||||
|
`#3015 <https://github.com/fmtlib/fmt/issues/3015>`_,
|
||||||
|
`#3021 <https://github.com/fmtlib/fmt/pull/3021>`_,
|
||||||
|
`#3023 <https://github.com/fmtlib/fmt/issues/3023>`_,
|
||||||
|
`#3024 <https://github.com/fmtlib/fmt/pull/3024>`_,
|
||||||
|
`#3029 <https://github.com/fmtlib/fmt/pull/3029>`_,
|
||||||
|
`#3043 <https://github.com/fmtlib/fmt/pull/3043>`_,
|
||||||
|
`#3052 <https://github.com/fmtlib/fmt/issues/3052>`_,
|
||||||
|
`#3053 <https://github.com/fmtlib/fmt/pull/3053>`_,
|
||||||
|
`#3054 <https://github.com/fmtlib/fmt/pull/3054>`_).
|
||||||
|
Thanks `@h-friederich (Hannes Friederich) <https://github.com/h-friederich>`_,
|
||||||
|
`@dimztimz (Dimitrij Mijoski) <https://github.com/dimztimz>`_,
|
||||||
|
`@olupton (Olli Lupton) <https://github.com/olupton>`_,
|
||||||
|
`@bernhardmgruber (Bernhard Manfred Gruber)
|
||||||
|
<https://github.com/bernhardmgruber>`_,
|
||||||
|
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
|
||||||
|
|
||||||
9.0.0 - 2022-07-04
|
9.0.0 - 2022-07-04
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
@ -19,7 +145,7 @@
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto answer = compile_time_itoa(0.42);
|
constexpr auto answer = compile_time_dtoa(0.42);
|
||||||
|
|
||||||
works with the default settings.
|
works with the default settings.
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,6 @@
|
||||||
.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg
|
.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg
|
||||||
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows
|
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows
|
||||||
|
|
||||||
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v?svg=true
|
|
||||||
:target: https://ci.appveyor.com/project/vitaut/fmt
|
|
||||||
|
|
||||||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
|
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
|
||||||
:alt: fmt is continuously fuzzed at oss-fuzz
|
:alt: fmt is continuously fuzzed at oss-fuzz
|
||||||
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
|
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
|
||||||
|
|
|
@ -203,7 +203,7 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
||||||
}
|
}
|
||||||
const auto min1 =
|
const auto min1 =
|
||||||
(std::numeric_limits<IntermediateRep>::min)() / Factor::num;
|
(std::numeric_limits<IntermediateRep>::min)() / Factor::num;
|
||||||
if (count < min1) {
|
if (!std::is_unsigned<IntermediateRep>::value && count < min1) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -1396,7 +1396,8 @@ inline bool isfinite(T) {
|
||||||
// Converts value to Int and checks that it's in the range [0, upper).
|
// Converts value to Int and checks that it's in the range [0, upper).
|
||||||
template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
inline Int to_nonnegative_int(T value, Int upper) {
|
inline Int to_nonnegative_int(T value, Int upper) {
|
||||||
FMT_ASSERT(value >= 0 && to_unsigned(value) <= to_unsigned(upper),
|
FMT_ASSERT(std::is_unsigned<Int>::value ||
|
||||||
|
(value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
|
||||||
"invalid value");
|
"invalid value");
|
||||||
(void)upper;
|
(void)upper;
|
||||||
return static_cast<Int>(value);
|
return static_cast<Int>(value);
|
||||||
|
@ -1776,7 +1777,7 @@ struct chrono_formatter {
|
||||||
format_to(std::back_inserter(buf), runtime("{:.{}f}"),
|
format_to(std::back_inserter(buf), runtime("{:.{}f}"),
|
||||||
std::fmod(val * static_cast<rep>(Period::num) /
|
std::fmod(val * static_cast<rep>(Period::num) /
|
||||||
static_cast<rep>(Period::den),
|
static_cast<rep>(Period::den),
|
||||||
60),
|
static_cast<rep>(60)),
|
||||||
num_fractional_digits);
|
num_fractional_digits);
|
||||||
if (negative) *out++ = '-';
|
if (negative) *out++ = '-';
|
||||||
if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
|
if (buf.size() < 2 || buf[1] == '.') *out++ = '0';
|
||||||
|
@ -2001,13 +2002,9 @@ template <typename Char, typename Duration>
|
||||||
struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
|
struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
|
||||||
Char> : formatter<std::tm, Char> {
|
Char> : formatter<std::tm, Char> {
|
||||||
FMT_CONSTEXPR formatter() {
|
FMT_CONSTEXPR formatter() {
|
||||||
this->do_parse(default_specs,
|
basic_string_view<Char> default_specs =
|
||||||
default_specs + sizeof(default_specs) / sizeof(Char));
|
detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>{};
|
||||||
}
|
this->do_parse(default_specs.begin(), default_specs.end());
|
||||||
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return this->do_parse(ctx.begin(), ctx.end(), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
|
@ -2015,15 +2012,8 @@ struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
|
||||||
FormatContext& ctx) const -> decltype(ctx.out()) {
|
FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||||
return formatter<std::tm, Char>::format(localtime(val), ctx);
|
return formatter<std::tm, Char>::format(localtime(val), ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr const Char default_specs[] = {'%', 'F', ' ', '%', 'T'};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename Duration>
|
|
||||||
constexpr const Char
|
|
||||||
formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
|
|
||||||
Char>::default_specs[];
|
|
||||||
|
|
||||||
template <typename Char> struct formatter<std::tm, Char> {
|
template <typename Char> struct formatter<std::tm, Char> {
|
||||||
private:
|
private:
|
||||||
enum class spec {
|
enum class spec {
|
||||||
|
@ -2035,13 +2025,18 @@ template <typename Char> struct formatter<std::tm, Char> {
|
||||||
basic_string_view<Char> specs;
|
basic_string_view<Char> specs;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <typename It>
|
template <typename It> FMT_CONSTEXPR auto do_parse(It begin, It end) -> It {
|
||||||
FMT_CONSTEXPR auto do_parse(It begin, It end, bool with_default = false)
|
|
||||||
-> It {
|
|
||||||
if (begin != end && *begin == ':') ++begin;
|
if (begin != end && *begin == ':') ++begin;
|
||||||
end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
|
end = detail::parse_chrono_format(begin, end, detail::tm_format_checker());
|
||||||
if (!with_default || end != begin)
|
// Replace default spec only if the new spec is not empty.
|
||||||
specs = {begin, detail::to_unsigned(end - begin)};
|
if (end != begin) specs = {begin, detail::to_unsigned(end - begin)};
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
auto end = this->do_parse(ctx.begin(), ctx.end());
|
||||||
// basic_string_view<>::compare isn't constexpr before C++17.
|
// basic_string_view<>::compare isn't constexpr before C++17.
|
||||||
if (specs.size() == 2 && specs[0] == Char('%')) {
|
if (specs.size() == 2 && specs[0] == Char('%')) {
|
||||||
if (specs[1] == Char('F'))
|
if (specs[1] == Char('F'))
|
||||||
|
@ -2052,12 +2047,6 @@ template <typename Char> struct formatter<std::tm, Char> {
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return this->do_parse(ctx.begin(), ctx.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const std::tm& tm, FormatContext& ctx) const
|
auto format(const std::tm& tm, FormatContext& ctx) const
|
||||||
-> decltype(ctx.out()) {
|
-> decltype(ctx.out()) {
|
||||||
|
|
|
@ -634,7 +634,7 @@ struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
|
||||||
|
|
||||||
**Example**::
|
**Example**::
|
||||||
|
|
||||||
fmt::print("Elapsed time: {s:.2f} seconds",
|
fmt::print("Elapsed time: {0:.2f} seconds",
|
||||||
fmt::styled(1.23, fmt::fg(fmt::color::green) |
|
fmt::styled(1.23, fmt::fg(fmt::color::green) |
|
||||||
fmt::bg(fmt::color::blue)));
|
fmt::bg(fmt::color::blue)));
|
||||||
\endrst
|
\endrst
|
||||||
|
|
|
@ -14,7 +14,7 @@ FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <typename Char, typename InputIt>
|
template <typename Char, typename InputIt>
|
||||||
inline counting_iterator copy_str(InputIt begin, InputIt end,
|
FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
|
||||||
counting_iterator it) {
|
counting_iterator it) {
|
||||||
return it + (end - begin);
|
return it + (end - begin);
|
||||||
}
|
}
|
||||||
|
@ -341,7 +341,7 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||||
next_arg_id);
|
next_arg_id);
|
||||||
auto f = formatter<T, Char>();
|
auto f = formatter<T, Char>();
|
||||||
auto end = f.parse(ctx);
|
auto end = f.parse(ctx);
|
||||||
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
|
return {f, pos + fmt::detail::to_unsigned(end - str.data()),
|
||||||
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
|
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,15 +397,22 @@ constexpr auto parse_replacement_field_then_tail(S format_str) {
|
||||||
return parse_tail<Args, END_POS + 1, NEXT_ID>(
|
return parse_tail<Args, END_POS + 1, NEXT_ID>(
|
||||||
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
|
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
|
||||||
format_str);
|
format_str);
|
||||||
} else if constexpr (c == ':') {
|
} else if constexpr (c != ':') {
|
||||||
|
FMT_THROW(format_error("expected ':'"));
|
||||||
|
} else {
|
||||||
constexpr auto result = parse_specs<typename field_type<T>::type>(
|
constexpr auto result = parse_specs<typename field_type<T>::type>(
|
||||||
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
|
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
|
||||||
return parse_tail<Args, result.end, result.next_arg_id>(
|
if constexpr (result.end >= str.size() || str[result.end] != '}') {
|
||||||
|
FMT_THROW(format_error("expected '}'"));
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return parse_tail<Args, result.end + 1, result.next_arg_id>(
|
||||||
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
|
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
|
||||||
result.fmt},
|
result.fmt},
|
||||||
format_str);
|
format_str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Compiles a non-empty format string and returns the compiled representation
|
// Compiles a non-empty format string and returns the compiled representation
|
||||||
// or unknown_format() on unrecognized input.
|
// or unknown_format() on unrecognized input.
|
||||||
|
@ -568,7 +575,8 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||||
|
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
size_t formatted_size(const S& format_str, const Args&... args) {
|
FMT_CONSTEXPR20 size_t formatted_size(const S& format_str,
|
||||||
|
const Args&... args) {
|
||||||
return fmt::format_to(detail::counting_iterator(), format_str, args...)
|
return fmt::format_to(detail::counting_iterator(), format_str, args...)
|
||||||
.count();
|
.count();
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
// 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 90000
|
#define FMT_VERSION 90100
|
||||||
|
|
||||||
#if defined(__clang__) && !defined(__ibmxl__)
|
#if defined(__clang__) && !defined(__ibmxl__)
|
||||||
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
||||||
|
@ -200,6 +200,9 @@
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// An inline std::forward replacement.
|
||||||
|
#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# define FMT_UNCHECKED_ITERATOR(It) \
|
# define FMT_UNCHECKED_ITERATOR(It) \
|
||||||
using _Unchecked_type = It // Mark iterator as checked.
|
using _Unchecked_type = It // Mark iterator as checked.
|
||||||
|
@ -273,7 +276,8 @@
|
||||||
#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS
|
#ifndef FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||||
# if defined(__cpp_nontype_template_args) && \
|
# if defined(__cpp_nontype_template_args) && \
|
||||||
((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \
|
((FMT_GCC_VERSION >= 903 && FMT_CPLUSPLUS >= 201709L) || \
|
||||||
__cpp_nontype_template_args >= 201911L)
|
__cpp_nontype_template_args >= 201911L) && \
|
||||||
|
!defined(__NVCOMPILER)
|
||||||
# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
|
# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1
|
||||||
# else
|
# else
|
||||||
# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
|
# define FMT_USE_NONTYPE_TEMPLATE_ARGS 0
|
||||||
|
@ -402,7 +406,7 @@ template <typename T> auto convert_for_visit(T) -> monostate { return {}; }
|
||||||
template <typename Int>
|
template <typename Int>
|
||||||
FMT_CONSTEXPR auto to_unsigned(Int value) ->
|
FMT_CONSTEXPR auto to_unsigned(Int value) ->
|
||||||
typename std::make_unsigned<Int>::type {
|
typename std::make_unsigned<Int>::type {
|
||||||
FMT_ASSERT(value >= 0, "negative value");
|
FMT_ASSERT(std::is_unsigned<Int>::value || value >= 0, "negative value");
|
||||||
return static_cast<typename std::make_unsigned<Int>::type>(value);
|
return static_cast<typename std::make_unsigned<Int>::type>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -707,8 +711,8 @@ class basic_format_parse_context : private ErrorHandler {
|
||||||
next_arg_id_ = -1;
|
next_arg_id_ = -1;
|
||||||
do_check_arg_id(id);
|
do_check_arg_id(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
|
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
|
||||||
|
FMT_CONSTEXPR void check_dynamic_spec(int arg_id);
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_error(const char* message) {
|
FMT_CONSTEXPR void on_error(const char* message) {
|
||||||
ErrorHandler::on_error(message);
|
ErrorHandler::on_error(message);
|
||||||
|
@ -735,7 +739,8 @@ class compile_parse_context
|
||||||
ErrorHandler eh = {}, int next_arg_id = 0)
|
ErrorHandler eh = {}, int next_arg_id = 0)
|
||||||
: base(format_str, eh, next_arg_id), num_args_(num_args), types_(types) {}
|
: base(format_str, eh, next_arg_id), num_args_(num_args), types_(types) {}
|
||||||
|
|
||||||
constexpr int num_args() const { return num_args_; }
|
constexpr auto num_args() const -> int { return num_args_; }
|
||||||
|
constexpr auto arg_type(int id) const -> type { return types_[id]; }
|
||||||
|
|
||||||
FMT_CONSTEXPR auto next_arg_id() -> int {
|
FMT_CONSTEXPR auto next_arg_id() -> int {
|
||||||
int id = base::next_arg_id();
|
int id = base::next_arg_id();
|
||||||
|
@ -748,6 +753,11 @@ class compile_parse_context
|
||||||
if (id >= num_args_) this->on_error("argument not found");
|
if (id >= num_args_) this->on_error("argument not found");
|
||||||
}
|
}
|
||||||
using base::check_arg_id;
|
using base::check_arg_id;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void check_dynamic_spec(int arg_id) {
|
||||||
|
if (arg_id < num_args_ && types_ && !is_integral_type(types_[arg_id]))
|
||||||
|
this->on_error("width/precision is not integer");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
FMT_END_DETAIL_NAMESPACE
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
@ -763,6 +773,15 @@ basic_format_parse_context<Char, ErrorHandler>::do_check_arg_id(int id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename ErrorHandler>
|
||||||
|
FMT_CONSTEXPR void
|
||||||
|
basic_format_parse_context<Char, ErrorHandler>::check_dynamic_spec(int arg_id) {
|
||||||
|
if (detail::is_constant_evaluated()) {
|
||||||
|
using context = detail::compile_parse_context<Char, ErrorHandler>;
|
||||||
|
static_cast<context*>(this)->check_dynamic_spec(arg_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Context> class basic_format_arg;
|
template <typename Context> class basic_format_arg;
|
||||||
template <typename Context> class basic_format_args;
|
template <typename Context> class basic_format_args;
|
||||||
template <typename Context> class dynamic_format_arg_store;
|
template <typename Context> class dynamic_format_arg_store;
|
||||||
|
@ -917,11 +936,11 @@ template <typename T> class buffer {
|
||||||
/** Appends data to the end of the buffer. */
|
/** Appends data to the end of the buffer. */
|
||||||
template <typename U> void append(const U* begin, const U* end);
|
template <typename U> void append(const U* begin, const U* end);
|
||||||
|
|
||||||
template <typename I> FMT_CONSTEXPR auto operator[](I index) -> T& {
|
template <typename Idx> FMT_CONSTEXPR auto operator[](Idx index) -> T& {
|
||||||
return ptr_[index];
|
return ptr_[index];
|
||||||
}
|
}
|
||||||
template <typename I>
|
template <typename Idx>
|
||||||
FMT_CONSTEXPR auto operator[](I index) const -> const T& {
|
FMT_CONSTEXPR auto operator[](Idx index) const -> const T& {
|
||||||
return ptr_[index];
|
return ptr_[index];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1649,6 +1668,11 @@ auto copy_str(InputIt begin, InputIt end, appender out) -> appender {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename R, typename OutputIt>
|
||||||
|
FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
|
||||||
|
return detail::copy_str<Char>(rng.begin(), rng.end(), out);
|
||||||
|
}
|
||||||
|
|
||||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
|
||||||
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
|
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
|
||||||
template <typename... Ts> struct void_t_impl { using type = void; };
|
template <typename... Ts> struct void_t_impl { using type = void; };
|
||||||
|
@ -1708,7 +1732,7 @@ constexpr auto encode_types() -> unsigned long long {
|
||||||
|
|
||||||
template <typename Context, typename T>
|
template <typename Context, typename T>
|
||||||
FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
|
FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
|
||||||
const auto& arg = arg_mapper<Context>().map(std::forward<T>(val));
|
const auto& arg = arg_mapper<Context>().map(FMT_FORWARD(val));
|
||||||
|
|
||||||
constexpr bool formattable_char =
|
constexpr bool formattable_char =
|
||||||
!std::is_same<decltype(arg), const unformattable_char&>::value;
|
!std::is_same<decltype(arg), const unformattable_char&>::value;
|
||||||
|
@ -1875,7 +1899,7 @@ class format_arg_store
|
||||||
data_{detail::make_arg<
|
data_{detail::make_arg<
|
||||||
is_packed, Context,
|
is_packed, Context,
|
||||||
detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
|
detail::mapped_type_constant<remove_cvref_t<T>, Context>::value>(
|
||||||
std::forward<T>(args))...} {
|
FMT_FORWARD(args))...} {
|
||||||
detail::init_named_args(data_.named_args(), 0, 0, args...);
|
detail::init_named_args(data_.named_args(), 0, 0, args...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1891,7 +1915,7 @@ class format_arg_store
|
||||||
template <typename Context = format_context, typename... Args>
|
template <typename Context = format_context, typename... Args>
|
||||||
constexpr auto make_format_args(Args&&... args)
|
constexpr auto make_format_args(Args&&... args)
|
||||||
-> format_arg_store<Context, remove_cvref_t<Args>...> {
|
-> format_arg_store<Context, remove_cvref_t<Args>...> {
|
||||||
return {std::forward<Args>(args)...};
|
return {FMT_FORWARD(args)...};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2240,11 +2264,14 @@ class dynamic_specs_handler
|
||||||
|
|
||||||
FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type {
|
FMT_CONSTEXPR auto make_arg_ref(int arg_id) -> arg_ref_type {
|
||||||
context_.check_arg_id(arg_id);
|
context_.check_arg_id(arg_id);
|
||||||
|
context_.check_dynamic_spec(arg_id);
|
||||||
return arg_ref_type(arg_id);
|
return arg_ref_type(arg_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type {
|
FMT_CONSTEXPR auto make_arg_ref(auto_id) -> arg_ref_type {
|
||||||
return arg_ref_type(context_.next_arg_id());
|
int arg_id = context_.next_arg_id();
|
||||||
|
context_.check_dynamic_spec(arg_id);
|
||||||
|
return arg_ref_type(arg_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id)
|
FMT_CONSTEXPR auto make_arg_ref(basic_string_view<char_type> arg_id)
|
||||||
|
@ -2270,12 +2297,15 @@ constexpr auto to_ascii(Char c) -> underlying_t<Char> {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR inline auto code_point_length_impl(char c) -> int {
|
||||||
|
return "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"
|
||||||
|
[static_cast<unsigned char>(c) >> 3];
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
|
FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int {
|
||||||
if (const_check(sizeof(Char) != 1)) return 1;
|
if (const_check(sizeof(Char) != 1)) return 1;
|
||||||
auto lengths =
|
int len = code_point_length_impl(static_cast<char>(*begin));
|
||||||
"\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4";
|
|
||||||
int len = lengths[static_cast<unsigned char>(*begin) >> 3];
|
|
||||||
|
|
||||||
// Compute the pointer to the next character early so that the next
|
// Compute the pointer to the next character early so that the next
|
||||||
// iteration can start working on the next character. Neither Clang
|
// iteration can start working on the next character. Neither Clang
|
||||||
|
@ -2803,7 +2833,8 @@ FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs<Char>& specs,
|
||||||
template <typename ErrorHandler = error_handler>
|
template <typename ErrorHandler = error_handler>
|
||||||
FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type,
|
FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type,
|
||||||
ErrorHandler&& eh = {}) -> bool {
|
ErrorHandler&& eh = {}) -> bool {
|
||||||
if (type == presentation_type::none || type == presentation_type::string)
|
if (type == presentation_type::none || type == presentation_type::string ||
|
||||||
|
type == presentation_type::debug)
|
||||||
return true;
|
return true;
|
||||||
if (type != presentation_type::pointer) eh.on_error("invalid type specifier");
|
if (type != presentation_type::pointer) eh.on_error("invalid type specifier");
|
||||||
return false;
|
return false;
|
||||||
|
@ -2921,7 +2952,10 @@ class format_string_checker {
|
||||||
basic_string_view<Char> format_str, ErrorHandler eh)
|
basic_string_view<Char> format_str, ErrorHandler eh)
|
||||||
: context_(format_str, num_args, types_, eh),
|
: context_(format_str, num_args, types_, eh),
|
||||||
parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
|
parse_funcs_{&parse_format_specs<Args, parse_context_type>...},
|
||||||
types_{type_constant<Args, char>::value...} {}
|
types_{
|
||||||
|
mapped_type_constant<Args,
|
||||||
|
basic_format_context<Char*, Char>>::value...} {
|
||||||
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
||||||
|
|
||||||
|
@ -3065,6 +3099,15 @@ struct formatter<T, Char,
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <detail::type U = detail::type_constant<T, Char>::value,
|
||||||
|
enable_if_t<(U == detail::type::string_type ||
|
||||||
|
U == detail::type::cstring_type ||
|
||||||
|
U == detail::type::char_type),
|
||||||
|
int> = 0>
|
||||||
|
FMT_CONSTEXPR void set_debug_format() {
|
||||||
|
specs_.type = presentation_type::debug;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
|
FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const
|
||||||
-> decltype(ctx.out());
|
-> decltype(ctx.out());
|
||||||
|
@ -3127,7 +3170,7 @@ template <typename Char, typename... Args> class basic_format_string {
|
||||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||||
// Workaround broken conversion on older gcc.
|
// Workaround broken conversion on older gcc.
|
||||||
template <typename...> using format_string = string_view;
|
template <typename...> using format_string = string_view;
|
||||||
inline auto runtime(string_view s) -> basic_string_view<char> { return s; }
|
inline auto runtime(string_view s) -> string_view { return s; }
|
||||||
#else
|
#else
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
using format_string = basic_format_string<char, type_identity_t<Args>...>;
|
using format_string = basic_format_string<char, type_identity_t<Args>...>;
|
||||||
|
|
|
@ -1337,7 +1337,7 @@ template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
|
||||||
|
|
||||||
if (r < deltai) {
|
if (r < deltai) {
|
||||||
// Exclude the right endpoint if necessary.
|
// Exclude the right endpoint if necessary.
|
||||||
if (r == 0 && z_mul.is_integer && !include_right_endpoint) {
|
if (r == 0 && (z_mul.is_integer & !include_right_endpoint)) {
|
||||||
--ret_value.significand;
|
--ret_value.significand;
|
||||||
r = float_info<T>::big_divisor;
|
r = float_info<T>::big_divisor;
|
||||||
goto small_divisor_case_label;
|
goto small_divisor_case_label;
|
||||||
|
@ -1346,27 +1346,12 @@ template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
|
||||||
goto small_divisor_case_label;
|
goto small_divisor_case_label;
|
||||||
} else {
|
} else {
|
||||||
// r == deltai; compare fractional parts.
|
// r == deltai; compare fractional parts.
|
||||||
const carrier_uint two_fl = two_fc - 1;
|
|
||||||
|
|
||||||
if (!include_left_endpoint ||
|
|
||||||
exponent < float_info<T>::case_fc_pm_half_lower_threshold ||
|
|
||||||
exponent > float_info<T>::divisibility_check_by_5_threshold) {
|
|
||||||
// If the left endpoint is not included, the condition for
|
|
||||||
// success is z^(f) < delta^(f) (odd parity).
|
|
||||||
// Otherwise, the inequalities on exponent ensure that
|
|
||||||
// x is not an integer, so if z^(f) >= delta^(f) (even parity), we in fact
|
|
||||||
// have strict inequality.
|
|
||||||
if (!cache_accessor<T>::compute_mul_parity(two_fl, cache, beta).parity) {
|
|
||||||
goto small_divisor_case_label;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const typename cache_accessor<T>::compute_mul_parity_result x_mul =
|
const typename cache_accessor<T>::compute_mul_parity_result x_mul =
|
||||||
cache_accessor<T>::compute_mul_parity(two_fl, cache, beta);
|
cache_accessor<T>::compute_mul_parity(two_fc - 1, cache, beta);
|
||||||
if (!x_mul.parity && !x_mul.is_integer) {
|
|
||||||
|
if (!(x_mul.parity | (x_mul.is_integer & include_left_endpoint)))
|
||||||
goto small_divisor_case_label;
|
goto small_divisor_case_label;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
ret_value.exponent = minus_k + float_info<T>::kappa + 1;
|
ret_value.exponent = minus_k + float_info<T>::kappa + 1;
|
||||||
|
|
||||||
// We may need to remove trailing zeros.
|
// We may need to remove trailing zeros.
|
||||||
|
@ -1404,7 +1389,7 @@ small_divisor_case_label:
|
||||||
// or equivalently, when y is an integer.
|
// or equivalently, when y is an integer.
|
||||||
if (y_mul.parity != approx_y_parity)
|
if (y_mul.parity != approx_y_parity)
|
||||||
--ret_value.significand;
|
--ret_value.significand;
|
||||||
else if (y_mul.is_integer && ret_value.significand % 2 != 0)
|
else if (y_mul.is_integer & (ret_value.significand % 2 != 0))
|
||||||
--ret_value.significand;
|
--ret_value.significand;
|
||||||
return ret_value;
|
return ret_value;
|
||||||
}
|
}
|
||||||
|
@ -1488,17 +1473,13 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
#ifdef _WIN32
|
||||||
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
|
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
|
||||||
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
|
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
|
||||||
void*, const void*, dword, dword*, void*);
|
void*, const void*, dword, dword*, void*);
|
||||||
} // namespace detail
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace detail {
|
FMT_FUNC bool write_console(std::FILE* f, string_view text) {
|
||||||
FMT_FUNC void print(std::FILE* f, string_view text) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
auto fd = _fileno(f);
|
auto fd = _fileno(f);
|
||||||
if (_isatty(fd)) {
|
if (_isatty(fd)) {
|
||||||
detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
|
detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
|
||||||
|
@ -1506,11 +1487,20 @@ FMT_FUNC void print(std::FILE* f, string_view text) {
|
||||||
if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
|
if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
|
||||||
u16.c_str(), static_cast<uint32_t>(u16.size()),
|
u16.c_str(), static_cast<uint32_t>(u16.size()),
|
||||||
&written, nullptr)) {
|
&written, nullptr)) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
// Fallback to fwrite on failure. It can happen if the output has been
|
|
||||||
// redirected to NUL.
|
|
||||||
}
|
}
|
||||||
|
// We return false if the file descriptor was not TTY, or it was but
|
||||||
|
// SetConsoleW failed which can happen if the output has been redirected to
|
||||||
|
// NUL. In both cases when we return false, we should attempt to do regular
|
||||||
|
// write via fwrite or std::ostream::write.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_FUNC void print(std::FILE* f, string_view text) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (write_console(f, text)) return;
|
||||||
#endif
|
#endif
|
||||||
detail::fwrite_fully(text.data(), 1, text.size(), f);
|
detail::fwrite_fully(text.data(), 1, text.size(), f);
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,6 +249,18 @@ FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename CharT, CharT... C> struct string_literal {
|
||||||
|
static constexpr CharT value[sizeof...(C)] = {C...};
|
||||||
|
constexpr operator basic_string_view<CharT>() const {
|
||||||
|
return {value, sizeof...(C)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#if FMT_CPLUSPLUS < 201703L
|
||||||
|
template <typename CharT, CharT... C>
|
||||||
|
constexpr CharT string_literal<CharT, C...>::value[sizeof...(C)];
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename Streambuf> class formatbuf : public Streambuf {
|
template <typename Streambuf> class formatbuf : public Streambuf {
|
||||||
private:
|
private:
|
||||||
using char_type = typename Streambuf::char_type;
|
using char_type = typename Streambuf::char_type;
|
||||||
|
@ -287,7 +299,8 @@ FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To {
|
||||||
if (is_constant_evaluated()) return std::bit_cast<To>(from);
|
if (is_constant_evaluated()) return std::bit_cast<To>(from);
|
||||||
#endif
|
#endif
|
||||||
auto to = To();
|
auto to = To();
|
||||||
std::memcpy(&to, &from, sizeof(to));
|
// The cast suppresses a bogus -Wclass-memaccess on GCC.
|
||||||
|
std::memcpy(static_cast<void*>(&to), &from, sizeof(to));
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,10 +379,12 @@ class uint128_fallback {
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback {
|
FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback {
|
||||||
if (shift == 64) return {0, hi_};
|
if (shift == 64) return {0, hi_};
|
||||||
|
if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64);
|
||||||
return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
|
return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback {
|
FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback {
|
||||||
if (shift == 64) return {lo_, 0};
|
if (shift == 64) return {lo_, 0};
|
||||||
|
if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64);
|
||||||
return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
|
return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& {
|
FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& {
|
||||||
|
@ -389,11 +404,11 @@ class uint128_fallback {
|
||||||
hi_ += (lo_ < n ? 1 : 0);
|
hi_ += (lo_ < n ? 1 : 0);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
#if FMT_HAS_BUILTIN(__builtin_addcll)
|
#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__)
|
||||||
unsigned long long carry;
|
unsigned long long carry;
|
||||||
lo_ = __builtin_addcll(lo_, n, 0, &carry);
|
lo_ = __builtin_addcll(lo_, n, 0, &carry);
|
||||||
hi_ += carry;
|
hi_ += carry;
|
||||||
#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64)
|
#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__)
|
||||||
unsigned long long result;
|
unsigned long long result;
|
||||||
auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
|
auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
|
||||||
lo_ = result;
|
lo_ = result;
|
||||||
|
@ -592,19 +607,23 @@ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e)
|
||||||
constexpr const int shiftc[] = {0, 18, 12, 6, 0};
|
constexpr const int shiftc[] = {0, 18, 12, 6, 0};
|
||||||
constexpr const int shifte[] = {0, 6, 4, 2, 0};
|
constexpr const int shifte[] = {0, 6, 4, 2, 0};
|
||||||
|
|
||||||
int len = code_point_length(s);
|
int len = code_point_length_impl(*s);
|
||||||
const char* next = s + len;
|
// Compute the pointer to the next character early so that the next
|
||||||
|
// iteration can start working on the next character. Neither Clang
|
||||||
|
// nor GCC figure out this reordering on their own.
|
||||||
|
const char* next = s + len + !len;
|
||||||
|
|
||||||
|
using uchar = unsigned char;
|
||||||
|
|
||||||
// Assume a four-byte character and load four bytes. Unused bits are
|
// Assume a four-byte character and load four bytes. Unused bits are
|
||||||
// shifted out.
|
// shifted out.
|
||||||
*c = uint32_t(s[0] & masks[len]) << 18;
|
*c = uint32_t(uchar(s[0]) & masks[len]) << 18;
|
||||||
*c |= uint32_t(s[1] & 0x3f) << 12;
|
*c |= uint32_t(uchar(s[1]) & 0x3f) << 12;
|
||||||
*c |= uint32_t(s[2] & 0x3f) << 6;
|
*c |= uint32_t(uchar(s[2]) & 0x3f) << 6;
|
||||||
*c |= uint32_t(s[3] & 0x3f) << 0;
|
*c |= uint32_t(uchar(s[3]) & 0x3f) << 0;
|
||||||
*c >>= shiftc[len];
|
*c >>= shiftc[len];
|
||||||
|
|
||||||
// Accumulate the various error conditions.
|
// Accumulate the various error conditions.
|
||||||
using uchar = unsigned char;
|
|
||||||
*e = (*c < mins[len]) << 6; // non-canonical encoding
|
*e = (*c < mins[len]) << 6; // non-canonical encoding
|
||||||
*e |= ((*c >> 11) == 0x1b) << 7; // surrogate half?
|
*e |= ((*c >> 11) == 0x1b) << 7; // surrogate half?
|
||||||
*e |= (*c > 0x10FFFF) << 8; // out of range?
|
*e |= (*c > 0x10FFFF) << 8; // out of range?
|
||||||
|
@ -628,8 +647,8 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) {
|
||||||
auto error = 0;
|
auto error = 0;
|
||||||
auto end = utf8_decode(buf_ptr, &cp, &error);
|
auto end = utf8_decode(buf_ptr, &cp, &error);
|
||||||
bool result = f(error ? invalid_code_point : cp,
|
bool result = f(error ? invalid_code_point : cp,
|
||||||
string_view(ptr, to_unsigned(end - buf_ptr)));
|
string_view(ptr, error ? 1 : to_unsigned(end - buf_ptr)));
|
||||||
return result ? end : nullptr;
|
return result ? (error ? buf_ptr + 1 : end) : nullptr;
|
||||||
};
|
};
|
||||||
auto p = s.data();
|
auto p = s.data();
|
||||||
const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
|
const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
|
||||||
|
@ -919,8 +938,11 @@ struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
#ifdef _WIN32
|
||||||
|
FMT_API bool write_console(std::FILE* f, string_view text);
|
||||||
|
#endif
|
||||||
FMT_API void print(std::FILE*, string_view);
|
FMT_API void print(std::FILE*, string_view);
|
||||||
}
|
} // namespace detail
|
||||||
|
|
||||||
/** A formatting error such as invalid format string. */
|
/** A formatting error such as invalid format string. */
|
||||||
FMT_CLASS_API
|
FMT_CLASS_API
|
||||||
|
@ -1213,7 +1235,7 @@ FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size)
|
||||||
|
|
||||||
template <typename Char, typename UInt, typename Iterator,
|
template <typename Char, typename UInt, typename Iterator,
|
||||||
FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
|
FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<Iterator>>::value)>
|
||||||
inline auto format_decimal(Iterator out, UInt value, int size)
|
FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size)
|
||||||
-> format_decimal_result<Iterator> {
|
-> format_decimal_result<Iterator> {
|
||||||
// Buffer is large enough to hold all digits (digits10 + 1).
|
// Buffer is large enough to hold all digits (digits10 + 1).
|
||||||
Char buffer[digits10<UInt>() + 1];
|
Char buffer[digits10<UInt>() + 1];
|
||||||
|
@ -1274,8 +1296,6 @@ template <> struct float_info<float> {
|
||||||
static const int small_divisor = 10;
|
static const int small_divisor = 10;
|
||||||
static const int min_k = -31;
|
static const int min_k = -31;
|
||||||
static const int max_k = 46;
|
static const int max_k = 46;
|
||||||
static const int divisibility_check_by_5_threshold = 39;
|
|
||||||
static const int case_fc_pm_half_lower_threshold = -1;
|
|
||||||
static const int shorter_interval_tie_lower_threshold = -35;
|
static const int shorter_interval_tie_lower_threshold = -35;
|
||||||
static const int shorter_interval_tie_upper_threshold = -35;
|
static const int shorter_interval_tie_upper_threshold = -35;
|
||||||
};
|
};
|
||||||
|
@ -1288,8 +1308,6 @@ template <> struct float_info<double> {
|
||||||
static const int small_divisor = 100;
|
static const int small_divisor = 100;
|
||||||
static const int min_k = -292;
|
static const int min_k = -292;
|
||||||
static const int max_k = 326;
|
static const int max_k = 326;
|
||||||
static const int divisibility_check_by_5_threshold = 86;
|
|
||||||
static const int case_fc_pm_half_lower_threshold = -2;
|
|
||||||
static const int shorter_interval_tie_lower_threshold = -77;
|
static const int shorter_interval_tie_lower_threshold = -77;
|
||||||
static const int shorter_interval_tie_upper_threshold = -77;
|
static const int shorter_interval_tie_upper_threshold = -77;
|
||||||
};
|
};
|
||||||
|
@ -1543,7 +1561,10 @@ FMT_CONSTEXPR inline fp get_cached_power(int min_exponent,
|
||||||
const int dec_exp_step = 8;
|
const int dec_exp_step = 8;
|
||||||
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
||||||
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
||||||
return {data::pow10_significands[index], data::pow10_exponents[index]};
|
// Using *(x + index) instead of x[index] avoids an issue with some compilers
|
||||||
|
// using the EDG frontend (e.g. nvhpc/22.3 in C++17 mode).
|
||||||
|
return {*(data::pow10_significands + index),
|
||||||
|
*(data::pow10_exponents + index)};
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
|
@ -1729,7 +1750,7 @@ inline auto find_escape(const char* begin, const char* end)
|
||||||
/* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
|
/* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \
|
||||||
/* Use a macro-like name to avoid shadowing warnings. */ \
|
/* Use a macro-like name to avoid shadowing warnings. */ \
|
||||||
struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \
|
struct FMT_GCC_VISIBILITY_HIDDEN FMT_COMPILE_STRING : base { \
|
||||||
using char_type = fmt::remove_cvref_t<decltype(s[0])>; \
|
using char_type FMT_MAYBE_UNUSED = fmt::remove_cvref_t<decltype(s[0])>; \
|
||||||
FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \
|
FMT_MAYBE_UNUSED FMT_CONSTEXPR explicit \
|
||||||
operator fmt::basic_string_view<char_type>() const { \
|
operator fmt::basic_string_view<char_type>() const { \
|
||||||
return fmt::detail_exported::compile_string_to_view<char_type>(s); \
|
return fmt::detail_exported::compile_string_to_view<char_type>(s); \
|
||||||
|
@ -1981,7 +2002,10 @@ auto write_int_localized(OutputIt out, UInt value, unsigned prefix,
|
||||||
grouping.count_separators(num_digits));
|
grouping.count_separators(num_digits));
|
||||||
return write_padded<align::right>(
|
return write_padded<align::right>(
|
||||||
out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
|
out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
|
||||||
if (prefix != 0) *it++ = static_cast<Char>(prefix);
|
if (prefix != 0) {
|
||||||
|
char sign = static_cast<char>(prefix);
|
||||||
|
*it++ = static_cast<Char>(sign);
|
||||||
|
}
|
||||||
return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
|
return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2123,29 +2147,30 @@ class counting_iterator {
|
||||||
FMT_UNCHECKED_ITERATOR(counting_iterator);
|
FMT_UNCHECKED_ITERATOR(counting_iterator);
|
||||||
|
|
||||||
struct value_type {
|
struct value_type {
|
||||||
template <typename T> void operator=(const T&) {}
|
template <typename T> FMT_CONSTEXPR void operator=(const T&) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
counting_iterator() : count_(0) {}
|
FMT_CONSTEXPR counting_iterator() : count_(0) {}
|
||||||
|
|
||||||
size_t count() const { return count_; }
|
FMT_CONSTEXPR size_t count() const { return count_; }
|
||||||
|
|
||||||
counting_iterator& operator++() {
|
FMT_CONSTEXPR counting_iterator& operator++() {
|
||||||
++count_;
|
++count_;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
counting_iterator operator++(int) {
|
FMT_CONSTEXPR counting_iterator operator++(int) {
|
||||||
auto it = *this;
|
auto it = *this;
|
||||||
++*this;
|
++*this;
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend counting_iterator operator+(counting_iterator it, difference_type n) {
|
FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it,
|
||||||
|
difference_type n) {
|
||||||
it.count_ += static_cast<size_t>(n);
|
it.count_ += static_cast<size_t>(n);
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_type operator*() const { return {}; }
|
FMT_CONSTEXPR value_type operator*() const { return {}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
template <typename Char, typename OutputIt>
|
||||||
|
@ -2991,7 +3016,7 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
|
||||||
upper = &upper_store;
|
upper = &upper_store;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool even = (value.f & 1) == 0;
|
int even = static_cast<int>((value.f & 1) == 0);
|
||||||
if (!upper) upper = &lower;
|
if (!upper) upper = &lower;
|
||||||
if ((flags & dragon::fixup) != 0) {
|
if ((flags & dragon::fixup) != 0) {
|
||||||
if (add_compare(numerator, *upper, denominator) + even <= 0) {
|
if (add_compare(numerator, *upper, denominator) + even <= 0) {
|
||||||
|
|
|
@ -10,6 +10,12 @@
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#if defined(_WIN32) && defined(__GLIBCXX__)
|
||||||
|
# include <ext/stdio_filebuf.h>
|
||||||
|
# include <ext/stdio_sync_filebuf.h>
|
||||||
|
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||||
|
# include <__std_stream>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
|
@ -51,43 +57,50 @@ struct is_streamable<
|
||||||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
|
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
|
||||||
: std::false_type {};
|
: std::false_type {};
|
||||||
|
|
||||||
template <typename Char> FILE* get_file(std::basic_filebuf<Char>&) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dummy_filebuf {
|
|
||||||
FILE* _Myfile;
|
|
||||||
};
|
|
||||||
template <typename T, typename U = int> struct ms_filebuf {
|
|
||||||
using type = dummy_filebuf;
|
|
||||||
};
|
|
||||||
template <typename T> struct ms_filebuf<T, decltype(T::_Myfile, 0)> {
|
|
||||||
using type = T;
|
|
||||||
};
|
|
||||||
using filebuf_type = ms_filebuf<std::filebuf>::type;
|
|
||||||
|
|
||||||
FILE* get_file(filebuf_type& buf);
|
|
||||||
|
|
||||||
// Generate a unique explicit instantion in every translation unit using a tag
|
// Generate a unique explicit instantion in every translation unit using a tag
|
||||||
// type in an anonymous namespace.
|
// type in an anonymous namespace.
|
||||||
namespace {
|
namespace {
|
||||||
struct filebuf_access_tag {};
|
struct file_access_tag {};
|
||||||
} // namespace
|
} // namespace
|
||||||
template <typename Tag, typename FileMemberPtr, FileMemberPtr file>
|
template <class Tag, class BufType, FILE* BufType::*FileMemberPtr>
|
||||||
class filebuf_access {
|
class file_access {
|
||||||
friend FILE* get_file(filebuf_type& buf) { return buf.*file; }
|
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
|
||||||
};
|
};
|
||||||
template class filebuf_access<filebuf_access_tag,
|
|
||||||
decltype(&filebuf_type::_Myfile),
|
|
||||||
&filebuf_type::_Myfile>;
|
|
||||||
|
|
||||||
inline bool write(std::filebuf& buf, fmt::string_view data) {
|
#if FMT_MSC_VERSION
|
||||||
FILE* f = get_file(buf);
|
template class file_access<file_access_tag, std::filebuf,
|
||||||
if (!f) return false;
|
&std::filebuf::_Myfile>;
|
||||||
print(f, data);
|
auto get_file(std::filebuf&) -> FILE*;
|
||||||
return true;
|
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||||
|
template class file_access<file_access_tag, std::__stdoutbuf<char>,
|
||||||
|
&std::__stdoutbuf<char>::__file_>;
|
||||||
|
auto get_file(std::__stdoutbuf<char>&) -> FILE*;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
|
||||||
|
#if FMT_MSC_VERSION
|
||||||
|
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
|
||||||
|
if (FILE* f = get_file(*buf)) return write_console(f, data);
|
||||||
|
#elif defined(_WIN32) && defined(__GLIBCXX__)
|
||||||
|
auto* rdbuf = os.rdbuf();
|
||||||
|
FILE* c_file;
|
||||||
|
if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
|
||||||
|
c_file = fbuf->file();
|
||||||
|
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
|
||||||
|
c_file = fbuf->file();
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
if (c_file) return write_console(c_file, data);
|
||||||
|
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||||
|
if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
|
||||||
|
if (FILE* f = get_file(*buf)) return write_console(f, data);
|
||||||
|
#else
|
||||||
|
ignore_unused(os, data);
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
inline bool write(std::wfilebuf&, fmt::basic_string_view<wchar_t>) {
|
inline bool write_ostream_unicode(std::wostream&,
|
||||||
|
fmt::basic_string_view<wchar_t>) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,10 +108,6 @@ inline bool write(std::wfilebuf&, fmt::basic_string_view<wchar_t>) {
|
||||||
// It is a separate function rather than a part of vprint to simplify testing.
|
// It is a separate function rather than a part of vprint to simplify testing.
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||||
if (const_check(FMT_MSC_VERSION)) {
|
|
||||||
auto filebuf = dynamic_cast<std::basic_filebuf<Char>*>(os.rdbuf());
|
|
||||||
if (filebuf && write(*filebuf, {buf.data(), buf.size()})) return;
|
|
||||||
}
|
|
||||||
const Char* buf_data = buf.data();
|
const Char* buf_data = buf.data();
|
||||||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||||
unsigned_streamsize size = buf.size();
|
unsigned_streamsize size = buf.size();
|
||||||
|
@ -130,6 +139,8 @@ template <typename T> struct streamed_view { const T& value; };
|
||||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
|
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
|
||||||
|
void set_debug_format() = delete;
|
||||||
|
|
||||||
template <typename T, typename OutputIt>
|
template <typename T, typename OutputIt>
|
||||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
|
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
|
||||||
-> OutputIt {
|
-> OutputIt {
|
||||||
|
@ -142,12 +153,13 @@ struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
|
||||||
|
|
||||||
using ostream_formatter = basic_ostream_formatter<char>;
|
using ostream_formatter = basic_ostream_formatter<char>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, typename Char>
|
||||||
struct formatter<detail::streamed_view<T>> : ostream_formatter {
|
struct formatter<detail::streamed_view<T>, Char>
|
||||||
|
: basic_ostream_formatter<Char> {
|
||||||
template <typename OutputIt>
|
template <typename OutputIt>
|
||||||
auto format(detail::streamed_view<T> view,
|
auto format(detail::streamed_view<T> view,
|
||||||
basic_format_context<OutputIt, char>& ctx) const -> OutputIt {
|
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
|
||||||
return ostream_formatter::format(view.value, ctx);
|
return basic_ostream_formatter<Char>::format(view.value, ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -175,6 +187,13 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||||
using basic_ostream_formatter<Char>::format;
|
using basic_ostream_formatter<Char>::format;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline void vprint_directly(std::ostream& os, string_view format_str,
|
||||||
|
format_args args) {
|
||||||
|
auto buffer = memory_buffer();
|
||||||
|
detail::vformat_to(buffer, format_str, args);
|
||||||
|
detail::write_buffer(os, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
FMT_MODULE_EXPORT template <typename Char>
|
FMT_MODULE_EXPORT template <typename Char>
|
||||||
|
@ -183,6 +202,7 @@ void vprint(std::basic_ostream<Char>& os,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
auto buffer = basic_memory_buffer<Char>();
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
detail::vformat_to(buffer, format_str, args);
|
detail::vformat_to(buffer, format_str, args);
|
||||||
|
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
|
||||||
detail::write_buffer(os, buffer);
|
detail::write_buffer(os, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +217,11 @@ void vprint(std::basic_ostream<Char>& os,
|
||||||
*/
|
*/
|
||||||
FMT_MODULE_EXPORT template <typename... T>
|
FMT_MODULE_EXPORT template <typename... T>
|
||||||
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
||||||
vprint(os, fmt, fmt::make_format_args(args...));
|
const auto& vargs = fmt::make_format_args(args...);
|
||||||
|
if (detail::is_utf8())
|
||||||
|
vprint(os, fmt, vargs);
|
||||||
|
else
|
||||||
|
detail::vprint_directly(os, fmt, vargs);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_MODULE_EXPORT
|
FMT_MODULE_EXPORT
|
||||||
|
|
|
@ -270,8 +270,8 @@ template <typename Range>
|
||||||
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
|
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
|
||||||
|
|
||||||
template <typename Range>
|
template <typename Range>
|
||||||
using uncvref_first_type = remove_cvref_t<
|
using uncvref_first_type =
|
||||||
decltype(std::declval<range_reference_type<Range>>().first)>;
|
remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>;
|
||||||
|
|
||||||
template <typename Range>
|
template <typename Range>
|
||||||
using uncvref_second_type = remove_cvref_t<
|
using uncvref_second_type = remove_cvref_t<
|
||||||
|
@ -326,18 +326,37 @@ struct formatter<TupleT, Char,
|
||||||
enable_if_t<fmt::is_tuple_like<TupleT>::value &&
|
enable_if_t<fmt::is_tuple_like<TupleT>::value &&
|
||||||
fmt::is_tuple_formattable<TupleT, Char>::value>> {
|
fmt::is_tuple_formattable<TupleT, Char>::value>> {
|
||||||
private:
|
private:
|
||||||
|
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
|
||||||
|
basic_string_view<Char> opening_bracket_ =
|
||||||
|
detail::string_literal<Char, '('>{};
|
||||||
|
basic_string_view<Char> closing_bracket_ =
|
||||||
|
detail::string_literal<Char, ')'>{};
|
||||||
|
|
||||||
// C++11 generic lambda for format().
|
// C++11 generic lambda for format().
|
||||||
template <typename FormatContext> struct format_each {
|
template <typename FormatContext> struct format_each {
|
||||||
template <typename T> void operator()(const T& v) {
|
template <typename T> void operator()(const T& v) {
|
||||||
if (i > 0) out = detail::write_delimiter(out);
|
if (i > 0) out = detail::copy_str<Char>(separator, out);
|
||||||
out = detail::write_range_entry<Char>(out, v);
|
out = detail::write_range_entry<Char>(out, v);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
int i;
|
int i;
|
||||||
typename FormatContext::iterator& out;
|
typename FormatContext::iterator& out;
|
||||||
|
basic_string_view<Char> separator;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
FMT_CONSTEXPR formatter() {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
|
||||||
|
separator_ = sep;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
|
||||||
|
basic_string_view<Char> close) {
|
||||||
|
opening_bracket_ = open;
|
||||||
|
closing_bracket_ = close;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
return ctx.begin();
|
return ctx.begin();
|
||||||
|
@ -347,9 +366,9 @@ struct formatter<TupleT, Char,
|
||||||
auto format(const TupleT& values, FormatContext& ctx) const
|
auto format(const TupleT& values, FormatContext& ctx) const
|
||||||
-> decltype(ctx.out()) {
|
-> decltype(ctx.out()) {
|
||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
*out++ = '(';
|
out = detail::copy_str<Char>(opening_bracket_, out);
|
||||||
detail::for_each(values, format_each<FormatContext>{0, out});
|
detail::for_each(values, format_each<FormatContext>{0, out, separator_});
|
||||||
*out++ = ')';
|
out = detail::copy_str<Char>(closing_bracket_, out);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -357,9 +376,8 @@ struct formatter<TupleT, Char,
|
||||||
template <typename T, typename Char> struct is_range {
|
template <typename T, typename Char> struct is_range {
|
||||||
static constexpr const bool value =
|
static constexpr const bool value =
|
||||||
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
||||||
!detail::is_map<T>::value &&
|
|
||||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
!std::is_convertible<T, detail::std_string_view<Char>>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
@ -390,40 +408,88 @@ using range_formatter_type = conditional_t<
|
||||||
template <typename R>
|
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>
|
||||||
|
struct is_formattable_delayed
|
||||||
|
: disjunction<
|
||||||
|
is_formattable<uncvref_type<maybe_const_range<R>>, Char>,
|
||||||
|
has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {};
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template <typename R, typename Char>
|
template <typename T, typename Char, typename Enable = void>
|
||||||
struct formatter<
|
struct range_formatter;
|
||||||
R, Char,
|
|
||||||
enable_if_t<
|
|
||||||
conjunction<fmt::is_range<R, Char>
|
|
||||||
// Workaround a bug in MSVC 2017 and earlier.
|
|
||||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920
|
|
||||||
,
|
|
||||||
disjunction<
|
|
||||||
is_formattable<detail::uncvref_type<detail::maybe_const_range<R>>,
|
|
||||||
Char>,
|
|
||||||
detail::has_fallback_formatter<
|
|
||||||
detail::uncvref_type<detail::maybe_const_range<R>>, Char>
|
|
||||||
>
|
|
||||||
#endif
|
|
||||||
>::value
|
|
||||||
>> {
|
|
||||||
|
|
||||||
using range_type = detail::maybe_const_range<R>;
|
template <typename T, typename Char>
|
||||||
using formatter_type =
|
struct range_formatter<
|
||||||
detail::range_formatter_type<Char, detail::uncvref_type<range_type>>;
|
T, Char,
|
||||||
formatter_type underlying_;
|
enable_if_t<conjunction<
|
||||||
|
std::is_same<T, remove_cvref_t<T>>,
|
||||||
|
disjunction<is_formattable<T, Char>,
|
||||||
|
detail::has_fallback_formatter<T, Char>>>::value>> {
|
||||||
|
private:
|
||||||
|
detail::range_formatter_type<Char, T> underlying_;
|
||||||
bool custom_specs_ = false;
|
bool custom_specs_ = false;
|
||||||
|
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
|
||||||
|
basic_string_view<Char> opening_bracket_ =
|
||||||
|
detail::string_literal<Char, '['>{};
|
||||||
|
basic_string_view<Char> closing_bracket_ =
|
||||||
|
detail::string_literal<Char, ']'>{};
|
||||||
|
|
||||||
|
template <class U>
|
||||||
|
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int)
|
||||||
|
-> decltype(u.set_debug_format()) {
|
||||||
|
u.set_debug_format();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class U>
|
||||||
|
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void maybe_set_debug_format() {
|
||||||
|
maybe_set_debug_format(underlying_, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR range_formatter() {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
|
||||||
|
return underlying_;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
|
||||||
|
separator_ = sep;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
|
||||||
|
basic_string_view<Char> close) {
|
||||||
|
opening_bracket_ = open;
|
||||||
|
closing_bracket_ = close;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
auto it = ctx.begin();
|
auto it = ctx.begin();
|
||||||
auto end = ctx.end();
|
auto end = ctx.end();
|
||||||
if (it == end || *it == '}') return it;
|
if (it == end || *it == '}') {
|
||||||
|
maybe_set_debug_format();
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*it == 'n') {
|
||||||
|
set_brackets({}, {});
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*it == '}') {
|
||||||
|
maybe_set_debug_format();
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
if (*it != ':')
|
if (*it != ':')
|
||||||
FMT_THROW(format_error("no top-level range formatters supported"));
|
FMT_THROW(format_error("no other top-level range formatters supported"));
|
||||||
|
|
||||||
custom_specs_ = true;
|
custom_specs_ = true;
|
||||||
++it;
|
++it;
|
||||||
|
@ -431,75 +497,100 @@ struct formatter<
|
||||||
return underlying_.parse(ctx);
|
return underlying_.parse(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename R, class FormatContext>
|
||||||
auto format(range_type& range, FormatContext& ctx) const
|
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||||
-> decltype(ctx.out()) {
|
|
||||||
Char prefix = detail::is_set<R>::value ? '{' : '[';
|
|
||||||
Char postfix = detail::is_set<R>::value ? '}' : ']';
|
|
||||||
detail::range_mapper<buffer_context<Char>> mapper;
|
detail::range_mapper<buffer_context<Char>> mapper;
|
||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
*out++ = prefix;
|
out = detail::copy_str<Char>(opening_bracket_, out);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
auto it = detail::range_begin(range);
|
auto it = detail::range_begin(range);
|
||||||
auto end = detail::range_end(range);
|
auto end = detail::range_end(range);
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
if (i > 0) out = detail::write_delimiter(out);
|
if (i > 0) out = detail::copy_str<Char>(separator_, out);
|
||||||
if (custom_specs_) {
|
;
|
||||||
ctx.advance_to(out);
|
ctx.advance_to(out);
|
||||||
out = underlying_.format(mapper.map(*it), ctx);
|
out = underlying_.format(mapper.map(*it), ctx);
|
||||||
} else {
|
|
||||||
out = detail::write_range_entry<Char>(out, *it);
|
|
||||||
}
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
*out++ = postfix;
|
out = detail::copy_str<Char>(closing_bracket_, out);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename Char>
|
enum class range_format { disabled, map, set, sequence, string, debug_string };
|
||||||
struct formatter<
|
|
||||||
T, Char,
|
namespace detail {
|
||||||
enable_if_t<conjunction<detail::is_map<T>
|
template <typename T> struct range_format_kind_ {
|
||||||
// Workaround a bug in MSVC 2017 and earlier.
|
static constexpr auto value = std::is_same<range_reference_type<T>, T>::value
|
||||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920
|
? range_format::disabled
|
||||||
,
|
: is_map<T>::value ? range_format::map
|
||||||
disjunction<
|
: is_set<T>::value ? range_format::set
|
||||||
is_formattable<detail::uncvref_first_type<T>, Char>,
|
: range_format::sequence;
|
||||||
detail::has_fallback_formatter<detail::uncvref_first_type<T>, Char>
|
};
|
||||||
>,
|
|
||||||
disjunction<
|
template <range_format K, typename R, typename Char, typename Enable = void>
|
||||||
is_formattable<detail::uncvref_second_type<T>, Char>,
|
struct range_default_formatter;
|
||||||
detail::has_fallback_formatter<detail::uncvref_second_type<T>, Char>
|
|
||||||
>
|
template <range_format K>
|
||||||
#endif
|
using range_format_constant = std::integral_constant<range_format, K>;
|
||||||
>::value
|
|
||||||
>> {
|
template <range_format K, typename R, typename Char>
|
||||||
template <typename ParseContext>
|
struct range_default_formatter<
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
K, R, Char,
|
||||||
return ctx.begin();
|
enable_if_t<(K == range_format::sequence || K == range_format::map ||
|
||||||
|
K == range_format::set)>> {
|
||||||
|
using range_type = detail::maybe_const_range<R>;
|
||||||
|
range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
|
||||||
|
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
|
||||||
|
detail::string_literal<Char, '}'>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
|
||||||
typename FormatContext, typename U,
|
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
|
||||||
FMT_ENABLE_IF(
|
detail::string_literal<Char, '}'>{});
|
||||||
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
underlying_.underlying().set_brackets({}, {});
|
||||||
const T, T>>::value)>
|
underlying_.underlying().set_separator(
|
||||||
auto format(U& map, FormatContext& ctx) const -> decltype(ctx.out()) {
|
detail::string_literal<Char, ':', ' '>{});
|
||||||
auto out = ctx.out();
|
|
||||||
*out++ = '{';
|
|
||||||
int i = 0;
|
|
||||||
for (const auto& item : map) {
|
|
||||||
if (i > 0) out = detail::write_delimiter(out);
|
|
||||||
out = detail::write_range_entry<Char>(out, item.first);
|
|
||||||
*out++ = ':';
|
|
||||||
*out++ = ' ';
|
|
||||||
out = detail::write_range_entry<Char>(out, item.second);
|
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
*out++ = '}';
|
|
||||||
return out;
|
FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return underlying_.parse(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(range_type& range, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
return underlying_.format(range, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename T, typename Char, typename Enable = void>
|
||||||
|
struct range_format_kind
|
||||||
|
: conditional_t<
|
||||||
|
is_range<T, Char>::value, detail::range_format_kind_<T>,
|
||||||
|
std::integral_constant<range_format, range_format::disabled>> {};
|
||||||
|
|
||||||
|
template <typename R, typename Char>
|
||||||
|
struct formatter<
|
||||||
|
R, Char,
|
||||||
|
enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
|
||||||
|
range_format::disabled>
|
||||||
|
// Workaround a bug in MSVC 2015 and earlier.
|
||||||
|
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
||||||
|
,
|
||||||
|
detail::is_formattable_delayed<R, Char>
|
||||||
|
#endif
|
||||||
|
>::value>>
|
||||||
|
: detail::range_default_formatter<range_format_kind<R, Char>::value, R,
|
||||||
|
Char> {
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
||||||
|
|
|
@ -57,10 +57,6 @@ inline void write_escaped_path<std::filesystem::path::value_type>(
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1920
|
|
||||||
// For MSVC 2017 and earlier using the partial specialization
|
|
||||||
// would cause an ambiguity error, therefore we provide it only
|
|
||||||
// conditionally.
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct formatter<std::filesystem::path, Char>
|
struct formatter<std::filesystem::path, Char>
|
||||||
: formatter<basic_string_view<Char>> {
|
: formatter<basic_string_view<Char>> {
|
||||||
|
@ -73,7 +69,6 @@ struct formatter<std::filesystem::path, Char>
|
||||||
basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
|
basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#define FMT_XCHAR_H_
|
#define FMT_XCHAR_H_
|
||||||
|
|
||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
|
@ -30,9 +29,11 @@ using wmemory_buffer = basic_memory_buffer<wchar_t>;
|
||||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||||
// Workaround broken conversion on older gcc.
|
// Workaround broken conversion on older gcc.
|
||||||
template <typename... Args> using wformat_string = wstring_view;
|
template <typename... Args> using wformat_string = wstring_view;
|
||||||
|
inline auto runtime(wstring_view s) -> wstring_view { return s; }
|
||||||
#else
|
#else
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
|
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
|
||||||
|
inline auto runtime(wstring_view s) -> basic_runtime<wchar_t> { return {{s}}; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <> struct is_char<wchar_t> : std::true_type {};
|
template <> struct is_char<wchar_t> : std::true_type {};
|
||||||
|
@ -82,20 +83,16 @@ auto vformat(basic_string_view<Char> format_str,
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 409
|
|
||||||
template <typename... Args>
|
|
||||||
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
|
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
|
||||||
return vformat(fmt, fmt::make_wformat_args(args...));
|
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass char_t as a default template parameter instead of using
|
// Pass char_t as a default template parameter instead of using
|
||||||
// std::basic_string<char_t<S>> to reduce the symbol size.
|
// std::basic_string<char_t<S>> to reduce the symbol size.
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>,
|
template <typename S, typename... Args, typename Char = char_t<S>,
|
||||||
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
|
||||||
|
!std::is_same<Char, wchar_t>::value)>
|
||||||
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
|
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
|
||||||
return vformat(detail::to_string_view(format_str),
|
return vformat(detail::to_string_view(format_str),
|
||||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||||
|
|
Loading…
Reference in New Issue