Update bundled fmt to 11.0.2 (#3236)

This commit is contained in:
Gabi Melman 2024-11-01 12:04:58 +02:00 committed by GitHub
parent 63d1884215
commit 85bdab0c18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 5460 additions and 4755 deletions

View File

@ -8,11 +8,13 @@
#ifndef FMT_ARGS_H_ #ifndef FMT_ARGS_H_
#define FMT_ARGS_H_ #define FMT_ARGS_H_
#include <functional> // std::reference_wrapper #ifndef FMT_MODULE
#include <memory> // std::unique_ptr # include <functional> // std::reference_wrapper
#include <vector> # include <memory> // std::unique_ptr
# include <vector>
#endif
#include "core.h" #include "format.h" // std_string_view
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -28,15 +30,18 @@ auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
return static_cast<const T&>(v); return static_cast<const T&>(v);
} }
class dynamic_arg_list { // node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for // 2022 (v17.10.0).
// templates it doesn't complain about inability to deduce single translation //
// unit for placing vtable. So storage_node_base is made a fake template. // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
template <typename = void> struct node { // templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So node is made a fake template.
template <typename = void> struct node {
virtual ~node() = default; virtual ~node() = default;
std::unique_ptr<node<>> next; std::unique_ptr<node<>> next;
}; };
class dynamic_arg_list {
template <typename T> struct typed_node : node<> { template <typename T> struct typed_node : node<> {
T value; T value;
@ -62,14 +67,10 @@ class dynamic_arg_list {
} // namespace detail } // namespace detail
/** /**
\rst * A dynamic list of formatting arguments with storage.
A dynamic version of `fmt::format_arg_store`. *
It's equipped with a storage to potentially temporary objects which lifetimes * It can be implicitly converted into `fmt::basic_format_args` for passing
could be shorter than the format arguments object. * into type-erased formatting functions such as `fmt::vformat`.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
*/ */
template <typename Context> template <typename Context>
class dynamic_format_arg_store class dynamic_format_arg_store
@ -147,21 +148,19 @@ class dynamic_format_arg_store
constexpr dynamic_format_arg_store() = default; constexpr dynamic_format_arg_store() = default;
/** /**
\rst * Adds an argument into the dynamic store for later passing to a formatting
Adds an argument into the dynamic store for later passing to a formatting * function.
function. *
* Note that custom types and string types (but not string views) are copied
Note that custom types and string types (but not string views) are copied * into the store dynamically allocating memory if necessary.
into the store dynamically allocating memory if necessary. *
* **Example**:
**Example**:: *
* fmt::dynamic_format_arg_store<fmt::format_context> store;
fmt::dynamic_format_arg_store<fmt::format_context> store; * store.push_back(42);
store.push_back(42); * store.push_back("abc");
store.push_back("abc"); * store.push_back(1.5f);
store.push_back(1.5f); * std::string result = fmt::vformat("{} and {} and {}", store);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/ */
template <typename T> void push_back(const T& arg) { template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value)) if (detail::const_check(need_copy<T>::value))
@ -171,19 +170,17 @@ class dynamic_format_arg_store
} }
/** /**
\rst * Adds a reference to the argument into the dynamic store for later passing
Adds a reference to the argument into the dynamic store for later passing to * to a formatting function.
a formatting function. *
* **Example**:
**Example**:: *
* fmt::dynamic_format_arg_store<fmt::format_context> store;
fmt::dynamic_format_arg_store<fmt::format_context> store; * char band[] = "Rolling Stones";
char band[] = "Rolling Stones"; * store.push_back(std::cref(band));
store.push_back(std::cref(band)); * band[9] = 'c'; // Changing str affects the output.
band[9] = 'c'; // Changing str affects the output. * std::string result = fmt::vformat("{}", store);
std::string result = fmt::vformat("{}", store); * // result == "Rolling Scones"
// result == "Rolling Scones"
\endrst
*/ */
template <typename T> void push_back(std::reference_wrapper<T> arg) { template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert( static_assert(
@ -193,9 +190,9 @@ class dynamic_format_arg_store
} }
/** /**
Adds named argument into the dynamic store for later passing to a formatting * Adds named argument into the dynamic store for later passing to a
function. ``std::reference_wrapper`` is supported to avoid copying of the * formatting function. `std::reference_wrapper` is supported to avoid
argument. The name is always copied into the store. * copying of the argument. The name is always copied into the store.
*/ */
template <typename T> template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) { void push_back(const detail::named_arg<char_type, T>& arg) {
@ -209,19 +206,15 @@ class dynamic_format_arg_store
} }
} }
/** Erase all elements from the store */ /// Erase all elements from the store.
void clear() { void clear() {
data_.clear(); data_.clear();
named_info_.clear(); named_info_.clear();
dynamic_args_ = detail::dynamic_arg_list(); dynamic_args_ = detail::dynamic_arg_list();
} }
/** /// Reserves space to store at least `new_cap` arguments including
\rst /// `new_cap_named` named arguments.
Reserves space to store at least *new_cap* arguments including
*new_cap_named* named arguments.
\endrst
*/
void reserve(size_t new_cap, size_t new_cap_named) { void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named, FMT_ASSERT(new_cap >= new_cap_named,
"Set of arguments includes set of named arguments"); "Set of arguments includes set of named arguments");

File diff suppressed because it is too large Load Diff

View File

@ -8,17 +8,19 @@
#ifndef FMT_CHRONO_H_ #ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_ #define FMT_CHRONO_H_
#include <algorithm> #ifndef FMT_MODULE
#include <chrono> # include <algorithm>
#include <cmath> // std::isfinite # include <chrono>
#include <cstring> // std::memcpy # include <cmath> // std::isfinite
#include <ctime> # include <cstring> // std::memcpy
#include <iterator> # include <ctime>
#include <locale> # include <iterator>
#include <ostream> # include <locale>
#include <type_traits> # include <ostream>
# include <type_traits>
#endif
#include "ostream.h" // formatbuf #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -94,10 +96,8 @@ FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
return static_cast<To>(from); return static_cast<To>(from);
} }
/** /// Converts From to To, without loss. If the dynamic value of from
* converts From to To, without loss. If the dynamic value of from /// can't be converted to To without loss, ec is set.
* can't be converted to To without loss, ec is set.
*/
template <typename To, typename From, template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value && FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed != std::numeric_limits<From>::is_signed !=
@ -185,9 +185,7 @@ FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To {
return from; return from;
} }
/** /// Safe duration cast between integral durations
* safe duration cast between integral durations
*/
template <typename To, typename FromRep, typename FromPeriod, template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value), FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)> FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
@ -237,9 +235,7 @@ auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
return ec ? To() : To(tocount); return ec ? To() : To(tocount);
} }
/** /// Safe duration_cast between floating point durations
* safe duration_cast between floating point durations
*/
template <typename To, typename FromRep, typename FromPeriod, template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value), FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)> FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
@ -329,6 +325,39 @@ inline auto localtime_s(...) -> null<> { return null<>(); }
inline auto gmtime_r(...) -> null<> { return null<>(); } inline auto gmtime_r(...) -> null<> { return null<>(); }
inline auto gmtime_s(...) -> null<> { return null<>(); } inline auto gmtime_s(...) -> null<> { return null<>(); }
// It is defined here and not in ostream.h because the latter has expensive
// includes.
template <typename Streambuf> class formatbuf : public Streambuf {
private:
using char_type = typename Streambuf::char_type;
using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
using int_type = typename Streambuf::int_type;
using traits_type = typename Streambuf::traits_type;
buffer<char_type>& buffer_;
public:
explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
protected:
// The put area is always empty. This makes the implementation simpler and has
// the advantage that the streambuf and the buffer are always in sync and
// sputc never writes into uninitialized memory. A disadvantage is that each
// call to sputc always results in a (virtual) call to overflow. There is no
// disadvantage here for sputn since this always results in a call to xsputn.
auto overflow(int_type ch) -> int_type override {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<char_type>(ch));
return ch;
}
auto xsputn(const char_type* s, streamsize count) -> streamsize override {
buffer_.append(s, s + count);
return count;
}
};
inline auto get_classic_locale() -> const std::locale& { inline auto get_classic_locale() -> const std::locale& {
static const auto& locale = std::locale::classic(); static const auto& locale = std::locale::classic();
return locale; return locale;
@ -362,11 +391,12 @@ void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
template <typename OutputIt> template <typename OutputIt>
auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
-> OutputIt { -> OutputIt {
if (detail::is_utf8() && loc != get_classic_locale()) { if (detail::use_utf8() && loc != get_classic_locale()) {
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
// gcc-4. // gcc-4.
#if FMT_MSC_VERSION != 0 || \ #if FMT_MSC_VERSION != 0 || \
(defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) (defined(__GLIBCXX__) && \
(!defined(_GLIBCXX_USE_DUAL_ABI) || _GLIBCXX_USE_DUAL_ABI == 0))
// The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
// and newer. // and newer.
using code_unit = wchar_t; using code_unit = wchar_t;
@ -382,9 +412,9 @@ auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>(); to_utf8<code_unit, basic_memory_buffer<char, unit_t::max_size * 4>>();
if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)})) if (!u.convert({unit.buf, to_unsigned(unit.end - unit.buf)}))
FMT_THROW(format_error("failed to format time")); FMT_THROW(format_error("failed to format time"));
return copy_str<char>(u.c_str(), u.c_str() + u.size(), out); return copy<char>(u.c_str(), u.c_str() + u.size(), out);
} }
return copy_str<char>(in.data(), in.data() + in.size(), out); return copy<char>(in.data(), in.data() + in.size(), out);
} }
template <typename Char, typename OutputIt, template <typename Char, typename OutputIt,
@ -393,7 +423,7 @@ auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc)
-> OutputIt { -> OutputIt {
codecvt_result<Char> unit; codecvt_result<Char> unit;
write_codecvt(unit, sv, loc); write_codecvt(unit, sv, loc);
return copy_str<Char>(unit.buf, unit.end, out); return copy<Char>(unit.buf, unit.end, out);
} }
template <typename Char, typename OutputIt, template <typename Char, typename OutputIt,
@ -482,9 +512,9 @@ auto to_time_t(
FMT_BEGIN_EXPORT FMT_BEGIN_EXPORT
/** /**
Converts given time since epoch as ``std::time_t`` value into calendar time, * Converts given time since epoch as `std::time_t` value into calendar time,
expressed in local time. Unlike ``std::localtime``, this function is * expressed in local time. Unlike `std::localtime`, this function is
thread-safe on most platforms. * thread-safe on most platforms.
*/ */
inline auto localtime(std::time_t time) -> std::tm { inline auto localtime(std::time_t time) -> std::tm {
struct dispatcher { struct dispatcher {
@ -531,9 +561,9 @@ inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
#endif #endif
/** /**
Converts given time since epoch as ``std::time_t`` value into calendar time, * Converts given time since epoch as `std::time_t` value into calendar time,
expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this
function is thread-safe on most platforms. * function is thread-safe on most platforms.
*/ */
inline auto gmtime(std::time_t time) -> std::tm { inline auto gmtime(std::time_t time) -> std::tm {
struct dispatcher { struct dispatcher {
@ -646,12 +676,10 @@ enum class numeric_system {
// Glibc extensions for formatting numeric values. // Glibc extensions for formatting numeric values.
enum class pad_type { enum class pad_type {
unspecified, // Pad a numeric result string with zeros (the default).
zero,
// Do not pad a numeric result string. // Do not pad a numeric result string.
none, none,
// Pad a numeric result string with zeros even if the conversion specifier
// character uses space-padding by default.
zero,
// Pad a numeric result string with spaces. // Pad a numeric result string with spaces.
space, space,
}; };
@ -659,7 +687,7 @@ enum class pad_type {
template <typename OutputIt> template <typename OutputIt>
auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt { auto write_padding(OutputIt out, pad_type pad, int width) -> OutputIt {
if (pad == pad_type::none) return out; if (pad == pad_type::none) return out;
return std::fill_n(out, width, pad == pad_type::space ? ' ' : '0'); return detail::fill_n(out, width, pad == pad_type::space ? ' ' : '0');
} }
template <typename OutputIt> template <typename OutputIt>
@ -675,8 +703,8 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end,
if (begin == end || *begin == '}') return begin; if (begin == end || *begin == '}') return begin;
if (*begin != '%') FMT_THROW(format_error("invalid format")); if (*begin != '%') FMT_THROW(format_error("invalid format"));
auto ptr = begin; auto ptr = begin;
pad_type pad = pad_type::unspecified;
while (ptr != end) { while (ptr != end) {
pad_type pad = pad_type::zero;
auto c = *ptr; auto c = *ptr;
if (c == '}') break; if (c == '}') break;
if (c != '%') { if (c != '%') {
@ -696,10 +724,6 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end,
pad = pad_type::none; pad = pad_type::none;
++ptr; ++ptr;
break; break;
case '0':
pad = pad_type::zero;
++ptr;
break;
} }
if (ptr == end) FMT_THROW(format_error("invalid format")); if (ptr == end) FMT_THROW(format_error("invalid format"));
c = *ptr++; c = *ptr++;
@ -759,22 +783,22 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end,
break; break;
// Day of the year/month: // Day of the year/month:
case 'U': case 'U':
handler.on_dec0_week_of_year(numeric_system::standard); handler.on_dec0_week_of_year(numeric_system::standard, pad);
break; break;
case 'W': case 'W':
handler.on_dec1_week_of_year(numeric_system::standard); handler.on_dec1_week_of_year(numeric_system::standard, pad);
break; break;
case 'V': case 'V':
handler.on_iso_week_of_year(numeric_system::standard); handler.on_iso_week_of_year(numeric_system::standard, pad);
break; break;
case 'j': case 'j':
handler.on_day_of_year(); handler.on_day_of_year();
break; break;
case 'd': case 'd':
handler.on_day_of_month(numeric_system::standard); handler.on_day_of_month(numeric_system::standard, pad);
break; break;
case 'e': case 'e':
handler.on_day_of_month_space(numeric_system::standard); handler.on_day_of_month(numeric_system::standard, pad_type::space);
break; break;
// Hour, minute, second: // Hour, minute, second:
case 'H': case 'H':
@ -871,19 +895,19 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end,
handler.on_dec_month(numeric_system::alternative); handler.on_dec_month(numeric_system::alternative);
break; break;
case 'U': case 'U':
handler.on_dec0_week_of_year(numeric_system::alternative); handler.on_dec0_week_of_year(numeric_system::alternative, pad);
break; break;
case 'W': case 'W':
handler.on_dec1_week_of_year(numeric_system::alternative); handler.on_dec1_week_of_year(numeric_system::alternative, pad);
break; break;
case 'V': case 'V':
handler.on_iso_week_of_year(numeric_system::alternative); handler.on_iso_week_of_year(numeric_system::alternative, pad);
break; break;
case 'd': case 'd':
handler.on_day_of_month(numeric_system::alternative); handler.on_day_of_month(numeric_system::alternative, pad);
break; break;
case 'e': case 'e':
handler.on_day_of_month_space(numeric_system::alternative); handler.on_day_of_month(numeric_system::alternative, pad_type::space);
break; break;
case 'w': case 'w':
handler.on_dec0_weekday(numeric_system::alternative); handler.on_dec0_weekday(numeric_system::alternative);
@ -936,12 +960,19 @@ template <typename Derived> struct null_chrono_spec_handler {
FMT_CONSTEXPR void on_abbr_month() { unsupported(); } FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
FMT_CONSTEXPR void on_full_month() { unsupported(); } FMT_CONSTEXPR void on_full_month() { unsupported(); }
FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {
FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } unsupported();
FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } }
FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {
unsupported();
}
FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {
unsupported();
}
FMT_CONSTEXPR void on_day_of_year() { unsupported(); } FMT_CONSTEXPR void on_day_of_year() { unsupported(); }
FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {
FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } unsupported();
}
FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
@ -979,12 +1010,11 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
FMT_CONSTEXPR void on_abbr_month() {} FMT_CONSTEXPR void on_abbr_month() {}
FMT_CONSTEXPR void on_full_month() {} FMT_CONSTEXPR void on_full_month() {}
FMT_CONSTEXPR void on_dec_month(numeric_system) {} FMT_CONSTEXPR void on_dec_month(numeric_system) {}
FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_day_of_year() {} FMT_CONSTEXPR void on_day_of_year() {}
FMT_CONSTEXPR void on_day_of_month(numeric_system) {} FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {}
FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
@ -1061,9 +1091,10 @@ inline auto to_nonnegative_int(T value, Int upper) -> Int {
} }
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 auto to_nonnegative_int(T value, Int upper) -> Int { inline auto to_nonnegative_int(T value, Int upper) -> Int {
if (value < 0 || value > static_cast<T>(upper)) auto int_value = static_cast<Int>(value);
if (int_value < 0 || value > static_cast<T>(upper))
FMT_THROW(format_error("invalid value")); FMT_THROW(format_error("invalid value"));
return static_cast<Int>(value); return int_value;
} }
constexpr auto pow10(std::uint32_t n) -> long long { constexpr auto pow10(std::uint32_t n) -> long long {
@ -1115,22 +1146,27 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
if (std::ratio_less<typename subsecond_precision::period, if (std::ratio_less<typename subsecond_precision::period,
std::chrono::seconds::period>::value) { std::chrono::seconds::period>::value) {
*out++ = '.'; *out++ = '.';
out = std::fill_n(out, leading_zeroes, '0'); out = detail::fill_n(out, leading_zeroes, '0');
out = format_decimal<Char>(out, n, num_digits).end; out = format_decimal<Char>(out, n, num_digits).end;
} }
} else { } else if (precision > 0) {
*out++ = '.'; *out++ = '.';
leading_zeroes = (std::min)(leading_zeroes, precision); leading_zeroes = (std::min)(leading_zeroes, precision);
out = std::fill_n(out, leading_zeroes, '0');
int remaining = precision - leading_zeroes; int remaining = precision - leading_zeroes;
if (remaining != 0 && remaining < num_digits) { out = detail::fill_n(out, leading_zeroes, '0');
n /= to_unsigned(detail::pow10(to_unsigned(num_digits - remaining))); if (remaining < num_digits) {
int num_truncated_digits = num_digits - remaining;
n /= to_unsigned(detail::pow10(to_unsigned(num_truncated_digits)));
if (n) {
out = format_decimal<Char>(out, n, remaining).end; out = format_decimal<Char>(out, n, remaining).end;
}
return; return;
} }
if (n) {
out = format_decimal<Char>(out, n, num_digits).end; out = format_decimal<Char>(out, n, num_digits).end;
remaining -= num_digits; remaining -= num_digits;
out = std::fill_n(out, remaining, '0'); }
out = detail::fill_n(out, remaining, '0');
} }
} }
@ -1281,7 +1317,8 @@ class tm_writer {
} }
uint32_or_64_or_128_t<long long> n = to_unsigned(year); uint32_or_64_or_128_t<long long> n = to_unsigned(year);
const int num_digits = count_digits(n); const int num_digits = count_digits(n);
if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); if (width > num_digits)
out_ = detail::fill_n(out_, width - num_digits, '0');
out_ = format_decimal<Char>(out_, n, num_digits).end; out_ = format_decimal<Char>(out_, n, num_digits).end;
} }
void write_year(long long year) { void write_year(long long year) {
@ -1364,7 +1401,7 @@ class tm_writer {
auto out() const -> OutputIt { return out_; } auto out() const -> OutputIt { return out_; }
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
out_ = copy_str<Char>(begin, end, out_); out_ = copy<Char>(begin, end, out_);
} }
void on_abbr_weekday() { void on_abbr_weekday() {
@ -1411,7 +1448,7 @@ class tm_writer {
*out_++ = ' '; *out_++ = ' ';
on_abbr_month(); on_abbr_month();
*out_++ = ' '; *out_++ = ' ';
on_day_of_month_space(numeric_system::standard); on_day_of_month(numeric_system::standard, pad_type::space);
*out_++ = ' '; *out_++ = ' ';
on_iso_time(); on_iso_time();
*out_++ = ' '; *out_++ = ' ';
@ -1437,7 +1474,7 @@ class tm_writer {
write_digit2_separated(buf, to_unsigned(tm_mon() + 1), write_digit2_separated(buf, to_unsigned(tm_mon() + 1),
to_unsigned(tm_mday()), to_unsigned(tm_mday()),
to_unsigned(split_year_lower(tm_year())), '/'); to_unsigned(split_year_lower(tm_year())), '/');
out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_); out_ = copy<Char>(std::begin(buf), std::end(buf), out_);
} }
void on_iso_date() { void on_iso_date() {
auto year = tm_year(); auto year = tm_year();
@ -1453,7 +1490,7 @@ class tm_writer {
write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100), write_digit2_separated(buf + 2, static_cast<unsigned>(year % 100),
to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()),
'-'); '-');
out_ = copy_str<Char>(std::begin(buf) + offset, std::end(buf), out_); out_ = copy<Char>(std::begin(buf) + offset, std::end(buf), out_);
} }
void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); } void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); }
@ -1498,24 +1535,26 @@ class tm_writer {
format_localized('m', 'O'); format_localized('m', 'O');
} }
void on_dec0_week_of_year(numeric_system ns) { void on_dec0_week_of_year(numeric_system ns, pad_type pad) {
if (is_classic_ || ns == numeric_system::standard) if (is_classic_ || ns == numeric_system::standard)
return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week,
pad);
format_localized('U', 'O'); format_localized('U', 'O');
} }
void on_dec1_week_of_year(numeric_system ns) { void on_dec1_week_of_year(numeric_system ns, pad_type pad) {
if (is_classic_ || ns == numeric_system::standard) { if (is_classic_ || ns == numeric_system::standard) {
auto wday = tm_wday(); auto wday = tm_wday();
write2((tm_yday() + days_per_week - write2((tm_yday() + days_per_week -
(wday == 0 ? (days_per_week - 1) : (wday - 1))) / (wday == 0 ? (days_per_week - 1) : (wday - 1))) /
days_per_week); days_per_week,
pad);
} else { } else {
format_localized('W', 'O'); format_localized('W', 'O');
} }
} }
void on_iso_week_of_year(numeric_system ns) { void on_iso_week_of_year(numeric_system ns, pad_type pad) {
if (is_classic_ || ns == numeric_system::standard) if (is_classic_ || ns == numeric_system::standard)
return write2(tm_iso_week_of_year()); return write2(tm_iso_week_of_year(), pad);
format_localized('V', 'O'); format_localized('V', 'O');
} }
@ -1529,20 +1568,11 @@ class tm_writer {
write1(yday / 100); write1(yday / 100);
write2(yday % 100); write2(yday % 100);
} }
void on_day_of_month(numeric_system ns) { void on_day_of_month(numeric_system ns, pad_type pad) {
if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); if (is_classic_ || ns == numeric_system::standard)
return write2(tm_mday(), pad);
format_localized('d', 'O'); format_localized('d', 'O');
} }
void on_day_of_month_space(numeric_system ns) {
if (is_classic_ || ns == numeric_system::standard) {
auto mday = to_unsigned(tm_mday()) % 100;
const char* d2 = digits2(mday);
*out_++ = mday < 10 ? ' ' : d2[0];
*out_++ = d2[1];
} else {
format_localized('e', 'O');
}
}
void on_24_hour(numeric_system ns, pad_type pad) { void on_24_hour(numeric_system ns, pad_type pad) {
if (is_classic_ || ns == numeric_system::standard) if (is_classic_ || ns == numeric_system::standard)
@ -1586,7 +1616,7 @@ class tm_writer {
char buf[8]; char buf[8];
write_digit2_separated(buf, to_unsigned(tm_hour12()), write_digit2_separated(buf, to_unsigned(tm_hour12()),
to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); to_unsigned(tm_min()), to_unsigned(tm_sec()), ':');
out_ = copy_str<Char>(std::begin(buf), std::end(buf), out_); out_ = copy<Char>(std::begin(buf), std::end(buf), out_);
*out_++ = ' '; *out_++ = ' ';
on_am_pm(); on_am_pm();
} else { } else {
@ -1601,7 +1631,7 @@ class tm_writer {
void on_iso_time() { void on_iso_time() {
on_24_hour_time(); on_24_hour_time();
*out_++ = ':'; *out_++ = ':';
on_second(numeric_system::standard, pad_type::unspecified); on_second(numeric_system::standard, pad_type::zero);
} }
void on_am_pm() { void on_am_pm() {
@ -1700,10 +1730,10 @@ auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt {
template <typename Char, typename Rep, typename OutputIt, template <typename Char, typename Rep, typename OutputIt,
FMT_ENABLE_IF(std::is_floating_point<Rep>::value)> FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt { auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt {
auto specs = format_specs<Char>(); auto specs = format_specs();
specs.precision = precision; specs.precision = precision;
specs.type = precision >= 0 ? presentation_type::fixed_lower specs.type =
: presentation_type::general_lower; precision >= 0 ? presentation_type::fixed : presentation_type::general;
return write<Char>(out, val, specs); return write<Char>(out, val, specs);
} }
@ -1744,8 +1774,10 @@ class get_locale {
public: public:
get_locale(bool localized, locale_ref loc) : has_locale_(localized) { get_locale(bool localized, locale_ref loc) : has_locale_(localized) {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
if (localized) if (localized)
::new (&locale_) std::locale(loc.template get<std::locale>()); ::new (&locale_) std::locale(loc.template get<std::locale>());
#endif
} }
~get_locale() { ~get_locale() {
if (has_locale_) locale_.~locale(); if (has_locale_) locale_.~locale();
@ -1840,7 +1872,7 @@ struct chrono_formatter {
} }
} }
void write(Rep value, int width, pad_type pad = pad_type::unspecified) { void write(Rep value, int width, pad_type pad = pad_type::zero) {
write_sign(); write_sign();
if (isnan(value)) return write_nan(); if (isnan(value)) return write_nan();
uint32_or_64_or_128_t<int> n = uint32_or_64_or_128_t<int> n =
@ -1890,11 +1922,10 @@ struct chrono_formatter {
void on_iso_week_based_year() {} void on_iso_week_based_year() {}
void on_iso_week_based_short_year() {} void on_iso_week_based_short_year() {}
void on_dec_month(numeric_system) {} void on_dec_month(numeric_system) {}
void on_dec0_week_of_year(numeric_system) {} void on_dec0_week_of_year(numeric_system, pad_type) {}
void on_dec1_week_of_year(numeric_system) {} void on_dec1_week_of_year(numeric_system, pad_type) {}
void on_iso_week_of_year(numeric_system) {} void on_iso_week_of_year(numeric_system, pad_type) {}
void on_day_of_month(numeric_system) {} void on_day_of_month(numeric_system, pad_type) {}
void on_day_of_month_space(numeric_system) {}
void on_day_of_year() { void on_day_of_year() {
if (handle_nan_inf()) return; if (handle_nan_inf()) return;
@ -1974,7 +2005,7 @@ struct chrono_formatter {
on_24_hour_time(); on_24_hour_time();
*out++ = ':'; *out++ = ':';
if (handle_nan_inf()) return; if (handle_nan_inf()) return;
on_second(numeric_system::standard, pad_type::unspecified); on_second(numeric_system::standard, pad_type::zero);
} }
void on_am_pm() { void on_am_pm() {
@ -1997,53 +2028,215 @@ struct chrono_formatter {
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907 #if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
using weekday = std::chrono::weekday; using weekday = std::chrono::weekday;
using day = std::chrono::day;
using month = std::chrono::month;
using year = std::chrono::year;
using year_month_day = std::chrono::year_month_day;
#else #else
// A fallback version of weekday. // A fallback version of weekday.
class weekday { class weekday {
private: private:
unsigned char value; unsigned char value_;
public: public:
weekday() = default; weekday() = default;
explicit constexpr weekday(unsigned wd) noexcept constexpr explicit weekday(unsigned wd) noexcept
: value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {} : value_(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
constexpr auto c_encoding() const noexcept -> unsigned { return value; } constexpr auto c_encoding() const noexcept -> unsigned { return value_; }
}; };
class year_month_day {}; class day {
private:
unsigned char value_;
public:
day() = default;
constexpr explicit day(unsigned d) noexcept
: value_(static_cast<unsigned char>(d)) {}
constexpr explicit operator unsigned() const noexcept { return value_; }
};
class month {
private:
unsigned char value_;
public:
month() = default;
constexpr explicit month(unsigned m) noexcept
: value_(static_cast<unsigned char>(m)) {}
constexpr explicit operator unsigned() const noexcept { return value_; }
};
class year {
private:
int value_;
public:
year() = default;
constexpr explicit year(int y) noexcept : value_(y) {}
constexpr explicit operator int() const noexcept { return value_; }
};
class year_month_day {
private:
fmt::year year_;
fmt::month month_;
fmt::day day_;
public:
year_month_day() = default;
constexpr year_month_day(const year& y, const month& m, const day& d) noexcept
: year_(y), month_(m), day_(d) {}
constexpr auto year() const noexcept -> fmt::year { return year_; }
constexpr auto month() const noexcept -> fmt::month { return month_; }
constexpr auto day() const noexcept -> fmt::day { return day_; }
};
#endif #endif
// A rudimentary weekday formatter. template <typename Char>
template <typename Char> struct formatter<weekday, Char> { struct formatter<weekday, Char> : private formatter<std::tm, Char> {
private: private:
bool localized = false; bool localized_ = false;
bool use_tm_formatter_ = false;
public: public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) { -> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end(); auto it = ctx.begin(), end = ctx.end();
if (begin != end && *begin == 'L') { if (it != end && *it == 'L') {
++begin; ++it;
localized = true; localized_ = true;
return it;
} }
return begin; use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
} }
template <typename FormatContext> template <typename FormatContext>
auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) {
auto time = std::tm(); auto time = std::tm();
time.tm_wday = static_cast<int>(wd.c_encoding()); time.tm_wday = static_cast<int>(wd.c_encoding());
detail::get_locale loc(localized, ctx.locale()); if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(localized_, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time); auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_abbr_weekday(); w.on_abbr_weekday();
return w.out(); return w.out();
} }
}; };
template <typename Char>
struct formatter<day, Char> : private formatter<std::tm, Char> {
private:
bool use_tm_formatter_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
}
template <typename FormatContext>
auto format(day d, FormatContext& ctx) const -> decltype(ctx.out()) {
auto time = std::tm();
time.tm_mday = static_cast<int>(static_cast<unsigned>(d));
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(false, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero);
return w.out();
}
};
template <typename Char>
struct formatter<month, Char> : private formatter<std::tm, Char> {
private:
bool localized_ = false;
bool use_tm_formatter_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
if (it != end && *it == 'L') {
++it;
localized_ = true;
return it;
}
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
}
template <typename FormatContext>
auto format(month m, FormatContext& ctx) const -> decltype(ctx.out()) {
auto time = std::tm();
time.tm_mon = static_cast<int>(static_cast<unsigned>(m)) - 1;
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(localized_, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_abbr_month();
return w.out();
}
};
template <typename Char>
struct formatter<year, Char> : private formatter<std::tm, Char> {
private:
bool use_tm_formatter_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
}
template <typename FormatContext>
auto format(year y, FormatContext& ctx) const -> decltype(ctx.out()) {
auto time = std::tm();
time.tm_year = static_cast<int>(y) - 1900;
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(false, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_year(detail::numeric_system::standard);
return w.out();
}
};
template <typename Char>
struct formatter<year_month_day, Char> : private formatter<std::tm, Char> {
private:
bool use_tm_formatter_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin(), end = ctx.end();
use_tm_formatter_ = it != end && *it != '}';
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
}
template <typename FormatContext>
auto format(year_month_day val, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto time = std::tm();
time.tm_year = static_cast<int>(val.year()) - 1900;
time.tm_mon = static_cast<int>(static_cast<unsigned>(val.month())) - 1;
time.tm_mday = static_cast<int>(static_cast<unsigned>(val.day()));
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
detail::get_locale loc(true, ctx.locale());
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
w.on_iso_date();
return w.out();
}
};
template <typename Rep, typename Period, typename Char> template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> { struct formatter<std::chrono::duration<Rep, Period>, Char> {
private: private:
format_specs<Char> specs_; format_specs specs_;
detail::arg_ref<Char> width_ref_; detail::arg_ref<Char> width_ref_;
detail::arg_ref<Char> precision_ref_; detail::arg_ref<Char> precision_ref_;
bool localized_ = false; bool localized_ = false;
@ -2117,27 +2310,26 @@ struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
template <typename FormatContext> template <typename FormatContext>
auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val, auto format(std::chrono::time_point<std::chrono::system_clock, Duration> val,
FormatContext& ctx) const -> decltype(ctx.out()) { FormatContext& ctx) const -> decltype(ctx.out()) {
std::tm tm = gmtime(val);
using period = typename Duration::period; using period = typename Duration::period;
if (detail::const_check( if (detail::const_check(
period::num != 1 || period::den != 1 || period::num == 1 && period::den == 1 &&
std::is_floating_point<typename Duration::rep>::value)) { !std::is_floating_point<typename Duration::rep>::value)) {
const auto epoch = val.time_since_epoch(); return formatter<std::tm, Char>::format(tm, ctx);
auto subsecs = detail::fmt_duration_cast<Duration>( }
Duration epoch = val.time_since_epoch();
Duration subsecs = detail::fmt_duration_cast<Duration>(
epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch)); epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch));
if (subsecs.count() < 0) { if (subsecs.count() < 0) {
auto second = auto second =
detail::fmt_duration_cast<Duration>(std::chrono::seconds(1)); detail::fmt_duration_cast<Duration>(std::chrono::seconds(1));
if (epoch.count() < ((Duration::min)() + second).count()) if (tm.tm_sec != 0)
FMT_THROW(format_error("duration is too small")); --tm.tm_sec;
subsecs += second; else
val -= second; tm = gmtime(val - second);
subsecs += detail::fmt_duration_cast<Duration>(std::chrono::seconds(1));
} }
return formatter<std::tm, Char>::do_format(tm, ctx, &subsecs);
return formatter<std::tm, Char>::do_format(gmtime(val), ctx, &subsecs);
}
return formatter<std::tm, Char>::format(gmtime(val), ctx);
} }
}; };
@ -2185,7 +2377,7 @@ struct formatter<std::chrono::time_point<std::chrono::utc_clock, Duration>,
template <typename Char> struct formatter<std::tm, Char> { template <typename Char> struct formatter<std::tm, Char> {
private: private:
format_specs<Char> specs_; format_specs specs_;
detail::arg_ref<Char> width_ref_; detail::arg_ref<Char> width_ref_;
protected: protected:

View File

@ -227,7 +227,7 @@ struct color_type {
}; };
} // namespace detail } // namespace detail
/** A text style consisting of foreground and background colors and emphasis. */ /// A text style consisting of foreground and background colors and emphasis.
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
@ -239,7 +239,7 @@ class text_style {
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) { } else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color")); report_error("can't OR a terminal color");
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
} }
@ -248,7 +248,7 @@ class text_style {
background_color = rhs.background_color; background_color = rhs.background_color;
} else if (rhs.set_background_color) { } else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb) if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color")); report_error("can't OR a terminal color");
background_color.value.rgb_color |= rhs.background_color.value.rgb_color; background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
} }
@ -310,13 +310,13 @@ class text_style {
emphasis ems; emphasis ems;
}; };
/** Creates a text style from the foreground (text) color. */ /// Creates a text style from the foreground (text) color.
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style { -> text_style {
return text_style(true, foreground); return text_style(true, foreground);
} }
/** Creates a text style from the background color. */ /// Creates a text style from the background color.
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style { -> text_style {
return text_style(false, background); return text_style(false, background);
@ -390,8 +390,8 @@ template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS auto end() const noexcept -> const Char* { FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + std::char_traits<Char>::length(buffer); return buffer + basic_string_view<Char>(buffer).size();
} }
private: private:
@ -441,9 +441,9 @@ template <typename T> struct styled_arg : detail::view {
}; };
template <typename Char> template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts, void vformat_to(
basic_string_view<Char> format_str, buffer<Char>& buf, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffered_context<type_identity_t<Char>>> args) {
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true; has_style = true;
@ -466,121 +466,92 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
} // namespace detail } // namespace detail
inline void vprint(std::FILE* f, const text_style& ts, string_view fmt, inline void vprint(FILE* f, const text_style& ts, string_view fmt,
format_args args) { format_args args) {
// Legacy wide streams are not supported.
auto buf = memory_buffer(); auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args); detail::vformat_to(buf, ts, fmt, args);
if (detail::is_utf8()) { print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
detail::print(f, string_view(buf.begin(), buf.size()));
return;
}
buf.push_back('\0');
int result = std::fputs(buf.data(), f);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
/** /**
\rst * Formats a string and prints it to the specified file stream using ANSI
Formats a string and prints it to the specified file stream using ANSI * escape sequences to specify text formatting.
escape sequences to specify text formatting. *
* **Example**:
**Example**:: *
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), * "Elapsed time: {0:.2f} seconds", 1.23);
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/ */
template <typename S, typename... Args, template <typename... T>
FMT_ENABLE_IF(detail::is_string<S>::value)> void print(FILE* f, const text_style& ts, format_string<T...> fmt,
void print(std::FILE* f, const text_style& ts, const S& format_str, T&&... args) {
const Args&... args) { vprint(f, ts, fmt, fmt::make_format_args(args...));
vprint(f, ts, format_str,
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
} }
/** /**
\rst * Formats a string and prints it to stdout using ANSI escape sequences to
Formats a string and prints it to stdout using ANSI escape sequences to * specify text formatting.
specify text formatting. *
* **Example**:
**Example**:: *
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), * "Elapsed time: {0:.2f} seconds", 1.23);
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/ */
template <typename S, typename... Args, template <typename... T>
FMT_ENABLE_IF(detail::is_string<S>::value)> void print(const text_style& ts, format_string<T...> fmt, T&&... args) {
void print(const text_style& ts, const S& format_str, const Args&... args) { return print(stdout, ts, fmt, std::forward<T>(args)...);
return print(stdout, ts, format_str, args...);
} }
template <typename S, typename Char = char_t<S>> inline auto vformat(const text_style& ts, string_view fmt, format_args args)
inline auto vformat( -> std::string {
const text_style& ts, const S& format_str, auto buf = memory_buffer();
basic_format_args<buffer_context<type_identity_t<Char>>> args) detail::vformat_to(buf, ts, fmt, args);
-> std::basic_string<Char> {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
return fmt::to_string(buf); return fmt::to_string(buf);
} }
/** /**
\rst * Formats arguments and returns the result as a string using ANSI escape
Formats arguments and returns the result as a string using ANSI * sequences to specify text formatting.
escape sequences to specify text formatting. *
* **Example**:
**Example**:: *
* ```
#include <fmt/color.h> * #include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), * std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42); * "The answer is {}", 42);
\endrst * ```
*/ */
template <typename S, typename... Args, typename Char = char_t<S>> template <typename... T>
inline auto format(const text_style& ts, const S& format_str, inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
const Args&... args) -> std::basic_string<Char> { -> std::string {
return fmt::vformat(ts, detail::to_string_view(format_str), return fmt::vformat(ts, fmt, fmt::make_format_args(args...));
fmt::make_format_args<buffer_context<Char>>(args...));
} }
/** /// Formats a string with the given text_style and writes the output to `out`.
Formats a string with the given text_style and writes the output to ``out``. template <typename OutputIt,
*/ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
template <typename OutputIt, typename Char, auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)> format_args args) -> OutputIt {
auto vformat_to(OutputIt out, const text_style& ts, auto&& buf = detail::get_buffer<char>(out);
basic_string_view<Char> format_str, detail::vformat_to(buf, ts, fmt, args);
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf, out); return detail::get_iterator(buf, out);
} }
/** /**
\rst * Formats arguments with the given text style, writes the result to the output
Formats arguments with the given text_style, writes the result to the output * iterator `out` and returns the iterator past the end of the output range.
iterator ``out`` and returns the iterator past the end of the output range. *
* **Example**:
**Example**:: *
* std::vector<char> out;
std::vector<char> out; * fmt::format_to(std::back_inserter(out),
fmt::format_to(std::back_inserter(out), * fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); */
\endrst template <typename OutputIt, typename... T,
*/ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
template < inline auto format_to(OutputIt out, const text_style& ts,
typename OutputIt, typename S, typename... Args, format_string<T...> fmt, T&&... args) -> OutputIt {
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value && return vformat_to(out, ts, fmt, fmt::make_format_args(args...));
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
} }
template <typename T, typename Char> template <typename T, typename Char>
@ -620,16 +591,14 @@ struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
}; };
/** /**
\rst * Returns an argument that will be formatted using ANSI escape sequences,
Returns an argument that will be formatted using ANSI escape sequences, * to be used in a formatting function.
to be used in a formatting function. *
* **Example**:
**Example**:: *
* fmt::print("Elapsed time: {0:.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
*/ */
template <typename T> template <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts) FMT_CONSTEXPR auto styled(const T& value, text_style ts)

View File

@ -8,39 +8,41 @@
#ifndef FMT_COMPILE_H_ #ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_ #define FMT_COMPILE_H_
#ifndef FMT_MODULE
# include <iterator> // std::back_inserter
#endif
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename InputIt>
FMT_CONSTEXPR inline auto copy_str(InputIt begin, InputIt end,
counting_iterator it) -> counting_iterator {
return it + (end - begin);
}
// A compile-time string which is compiled into fast formatting code. // A compile-time string which is compiled into fast formatting code.
class compiled_string {}; FMT_EXPORT class compiled_string {};
namespace detail {
template <typename T, typename InputIt>
FMT_CONSTEXPR inline auto copy(InputIt begin, InputIt end, counting_iterator it)
-> counting_iterator {
return it + (end - begin);
}
template <typename S> template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {}; struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/** /**
\rst * Converts a string literal `s` into a format string that will be parsed at
Converts a string literal *s* into a format string that will be parsed at * compile time and converted into efficient formatting code. Requires C++17
compile time and converted into efficient formatting code. Requires C++17 * `constexpr if` compiler support.
``constexpr if`` compiler support. *
* **Example**:
**Example**:: *
* // Converts 42 into std::string using the most efficient method and no
// Converts 42 into std::string using the most efficient method and no * // runtime format string processing.
// runtime format string processing. * std::string s = fmt::format(FMT_COMPILE("{}"), 42);
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/ */
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) \ # define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string, explicit)
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
#else #else
# define FMT_COMPILE(s) FMT_STRING(s) # define FMT_COMPILE(s) FMT_STRING(s)
#endif #endif
@ -144,9 +146,9 @@ template <typename Char, typename T, int N> struct field {
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const { constexpr OutputIt format(OutputIt out, const Args&... args) const {
const T& arg = get_arg_checked<T, N>(args...); const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible_v<T, basic_string_view<Char>>) { if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg); auto s = basic_string_view<Char>(arg);
return copy_str<Char>(s.begin(), s.end(), out); return copy<Char>(s.begin(), s.end(), out);
} }
return write<Char>(out, arg); return write<Char>(out, arg);
} }
@ -236,13 +238,12 @@ constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
} }
template <typename Args, size_t POS, int ID, typename S> template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str); constexpr auto compile_format_string(S fmt);
template <typename Args, size_t POS, int ID, typename T, typename S> template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) { constexpr auto parse_tail(T head, S fmt) {
if constexpr (POS != if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
basic_string_view<typename S::char_type>(format_str).size()) { constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>()) unknown_format>())
return tail; return tail;
@ -313,14 +314,13 @@ struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID, template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S> typename S>
constexpr auto parse_replacement_field_then_tail(S format_str) { constexpr auto parse_replacement_field_then_tail(S fmt) {
using char_type = typename S::char_type; using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str); constexpr auto str = basic_string_view<char_type>(fmt);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') { if constexpr (c == '}') {
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>(), fmt);
format_str);
} else if constexpr (c != ':') { } else if constexpr (c != ':') {
FMT_THROW(format_error("expected ':'")); FMT_THROW(format_error("expected ':'"));
} else { } else {
@ -333,7 +333,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) {
return parse_tail<Args, result.end + 1, result.next_arg_id>( 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); fmt);
} }
} }
} }
@ -341,22 +341,21 @@ constexpr auto parse_replacement_field_then_tail(S 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.
template <typename Args, size_t POS, int ID, typename S> template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) { constexpr auto compile_format_string(S fmt) {
using char_type = typename S::char_type; using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str); constexpr auto str = basic_string_view<char_type>(fmt);
if constexpr (str[POS] == '{') { if constexpr (str[POS] == '{') {
if constexpr (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '{' in format string")); FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') { if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id, static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
constexpr auto next_id = constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id; ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args, return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>( POS + 1, ID, next_id>(fmt);
format_str);
} else { } else {
constexpr auto arg_id_result = constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size()); parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
@ -372,7 +371,7 @@ constexpr auto compile_format_string(S format_str) {
return parse_replacement_field_then_tail<get_type<arg_index, Args>, return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos, Args, arg_id_end_pos,
arg_index, manual_indexing_id>( arg_index, manual_indexing_id>(
format_str); fmt);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { } else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
constexpr auto arg_index = constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
@ -381,11 +380,11 @@ constexpr auto compile_format_string(S format_str) {
ID != manual_indexing_id ? ID + 1 : manual_indexing_id; ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail< return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos, decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str); arg_index, next_id>(fmt);
} else if constexpr (c == '}') { } else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>( return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name}, runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str); fmt);
} else if constexpr (c == ':') { } else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing return unknown_format(); // no type info for specs parsing
} }
@ -394,29 +393,26 @@ constexpr auto compile_format_string(S format_str) {
} else if constexpr (str[POS] == '}') { } else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '}' in format string")); FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else { } else {
constexpr auto end = parse_text(str, POS + 1); constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) { if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
format_str);
} else { } else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
format_str);
} }
} }
} }
template <typename... Args, typename S, template <typename... Args, typename S,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) { constexpr auto compile(S fmt) {
constexpr auto str = basic_string_view<typename S::char_type>(format_str); constexpr auto str = basic_string_view<typename S::char_type>(fmt);
if constexpr (str.size() == 0) { if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0); return detail::make_text(str, 0, 0);
} else { } else {
constexpr auto result = constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>( detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
format_str);
return result; return result;
} }
} }
@ -488,35 +484,33 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
auto format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args) auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
-> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits; using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n); auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), format_str, fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
std::forward<Args>(args)...);
return {buf.out(), buf.count()}; return {buf.out(), buf.count()};
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& format_str, const Args&... args) FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t { -> size_t {
return fmt::format_to(detail::counting_iterator(), format_str, args...) return fmt::format_to(detail::counting_iterator(), fmt, args...).count();
.count();
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) { void print(std::FILE* f, const S& fmt, const Args&... args) {
memory_buffer buffer; memory_buffer buffer;
fmt::format_to(std::back_inserter(buffer), format_str, args...); fmt::format_to(std::back_inserter(buffer), fmt, args...);
detail::print(f, {buffer.data(), buffer.size()}); detail::print(f, {buffer.data(), buffer.size()});
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) { void print(const S& fmt, const Args&... args) {
print(stdout, format_str, args...); print(stdout, fmt, args...);
} }
#if FMT_USE_NONTYPE_TEMPLATE_ARGS #if FMT_USE_NONTYPE_TEMPLATE_ARGS

File diff suppressed because it is too large Load Diff

View File

@ -8,17 +8,19 @@
#ifndef FMT_FORMAT_INL_H_ #ifndef FMT_FORMAT_INL_H_
#define FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_
#include <algorithm> #ifndef FMT_MODULE
#include <cerrno> // errno # include <algorithm>
#include <climits> # include <cerrno> // errno
#include <cmath> # include <climits>
#include <exception> # include <cmath>
# include <exception>
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR # if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale> # include <locale>
# endif
#endif #endif
#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR) #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
# include <io.h> // _isatty # include <io.h> // _isatty
#endif #endif
@ -36,10 +38,6 @@ FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
std::terminate(); std::terminate();
} }
FMT_FUNC void throw_format_error(const char* message) {
FMT_THROW(format_error(message));
}
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code, FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) noexcept { string_view message) noexcept {
// Report error code making sure that the output fits into // Report error code making sure that the output fits into
@ -56,7 +54,7 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
++error_code_size; ++error_code_size;
} }
error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
auto it = buffer_appender<char>(out); auto it = appender(out);
if (message.size() <= inline_buffer_size - error_code_size) if (message.size() <= inline_buffer_size - error_code_size)
fmt::format_to(it, FMT_STRING("{}{}"), message, SEP); fmt::format_to(it, FMT_STRING("{}{}"), message, SEP);
fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
@ -113,8 +111,12 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
#endif #endif
FMT_FUNC auto write_loc(appender out, loc_value value, FMT_FUNC auto write_loc(appender out, loc_value value,
const format_specs<>& specs, locale_ref loc) -> bool { const format_specs& specs, locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifdef FMT_STATIC_THOUSANDS_SEPARATOR
value.visit(loc_writer<>{
out, specs, std::string(1, FMT_STATIC_THOUSANDS_SEPARATOR), "\3", "."});
return true;
#else
auto locale = loc.get<std::locale>(); auto locale = loc.get<std::locale>();
// We cannot use the num_put<char> facet because it may produce output in // We cannot use the num_put<char> facet because it may produce output in
// a wrong encoding. // a wrong encoding.
@ -123,10 +125,13 @@ FMT_FUNC auto write_loc(appender out, loc_value value,
return std::use_facet<facet>(locale).put(out, value, specs); return std::use_facet<facet>(locale).put(out, value, specs);
return facet(locale).put(out, value, specs); return facet(locale).put(out, value, specs);
#endif #endif
return false;
} }
} // namespace detail } // namespace detail
FMT_FUNC void report_error(const char* message) {
FMT_THROW(format_error(message));
}
template <typename Locale> typename Locale::id format_facet<Locale>::id; template <typename Locale> typename Locale::id format_facet<Locale>::id;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
@ -138,7 +143,7 @@ template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
template <> template <>
FMT_API FMT_FUNC auto format_facet<std::locale>::do_put( FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
appender out, loc_value val, const format_specs<>& specs) const -> bool { appender out, loc_value val, const format_specs& specs) const -> bool {
return val.visit( return val.visit(
detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_});
} }
@ -1411,7 +1416,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
const char* message) noexcept { const char* message) noexcept {
FMT_TRY { FMT_TRY {
auto ec = std::error_code(error_code, std::generic_category()); auto ec = std::error_code(error_code, std::generic_category());
write(std::back_inserter(out), std::system_error(ec, message).what()); detail::write(appender(out), std::system_error(ec, message).what());
return; return;
} }
FMT_CATCH(...) {} FMT_CATCH(...) {}
@ -1432,9 +1437,242 @@ FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
} }
namespace detail { namespace detail {
#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR)
template <typename T> struct span {
T* data;
size_t size;
};
template <typename F> auto flockfile(F* f) -> decltype(_lock_file(f)) {
_lock_file(f);
}
template <typename F> auto funlockfile(F* f) -> decltype(_unlock_file(f)) {
_unlock_file(f);
}
#ifndef getc_unlocked
template <typename F> auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) {
return _fgetc_nolock(f);
}
#endif
template <typename F = FILE, typename Enable = void>
struct has_flockfile : std::false_type {};
template <typename F>
struct has_flockfile<F, void_t<decltype(flockfile(&std::declval<F&>()))>>
: std::true_type {};
// A FILE wrapper. F is FILE defined as a template parameter to make system API
// detection work.
template <typename F> class file_base {
public:
F* file_;
public:
file_base(F* file) : file_(file) {}
operator F*() const { return file_; }
// Reads a code unit from the stream.
auto get() -> int {
int result = getc_unlocked(file_);
if (result == EOF && ferror(file_) != 0)
FMT_THROW(system_error(errno, FMT_STRING("getc failed")));
return result;
}
// Puts the code unit back into the stream buffer.
void unget(char c) {
if (ungetc(c, file_) == EOF)
FMT_THROW(system_error(errno, FMT_STRING("ungetc failed")));
}
void flush() { fflush(this->file_); }
};
// A FILE wrapper for glibc.
template <typename F> class glibc_file : public file_base<F> {
private:
enum {
line_buffered = 0x200, // _IO_LINE_BUF
unbuffered = 2 // _IO_UNBUFFERED
};
public:
using file_base<F>::file_base;
auto is_buffered() const -> bool {
return (this->file_->_flags & unbuffered) == 0;
}
void init_buffer() {
if (this->file_->_IO_write_ptr) return;
// Force buffer initialization by placing and removing a char in a buffer.
putc_unlocked(0, this->file_);
--this->file_->_IO_write_ptr;
}
// Returns the file's read buffer.
auto get_read_buffer() const -> span<const char> {
auto ptr = this->file_->_IO_read_ptr;
return {ptr, to_unsigned(this->file_->_IO_read_end - ptr)};
}
// Returns the file's write buffer.
auto get_write_buffer() const -> span<char> {
auto ptr = this->file_->_IO_write_ptr;
return {ptr, to_unsigned(this->file_->_IO_buf_end - ptr)};
}
void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; }
bool needs_flush() const {
if ((this->file_->_flags & line_buffered) == 0) return false;
char* end = this->file_->_IO_write_end;
return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end));
}
void flush() { fflush_unlocked(this->file_); }
};
// A FILE wrapper for Apple's libc.
template <typename F> class apple_file : public file_base<F> {
private:
enum {
line_buffered = 1, // __SNBF
unbuffered = 2 // __SLBF
};
public:
using file_base<F>::file_base;
auto is_buffered() const -> bool {
return (this->file_->_flags & unbuffered) == 0;
}
void init_buffer() {
if (this->file_->_p) return;
// Force buffer initialization by placing and removing a char in a buffer.
putc_unlocked(0, this->file_);
--this->file_->_p;
++this->file_->_w;
}
auto get_read_buffer() const -> span<const char> {
return {reinterpret_cast<char*>(this->file_->_p),
to_unsigned(this->file_->_r)};
}
auto get_write_buffer() const -> span<char> {
return {reinterpret_cast<char*>(this->file_->_p),
to_unsigned(this->file_->_bf._base + this->file_->_bf._size -
this->file_->_p)};
}
void advance_write_buffer(size_t size) {
this->file_->_p += size;
this->file_->_w -= size;
}
bool needs_flush() const {
if ((this->file_->_flags & line_buffered) == 0) return false;
return memchr(this->file_->_p + this->file_->_w, '\n',
to_unsigned(-this->file_->_w));
}
};
// A fallback FILE wrapper.
template <typename F> class fallback_file : public file_base<F> {
private:
char next_; // The next unconsumed character in the buffer.
bool has_next_ = false;
public:
using file_base<F>::file_base;
auto is_buffered() const -> bool { return false; }
auto needs_flush() const -> bool { return false; }
void init_buffer() {}
auto get_read_buffer() const -> span<const char> {
return {&next_, has_next_ ? 1u : 0u};
}
auto get_write_buffer() const -> span<char> { return {nullptr, 0}; }
void advance_write_buffer(size_t) {}
auto get() -> int {
has_next_ = false;
return file_base<F>::get();
}
void unget(char c) {
file_base<F>::unget(c);
next_ = c;
has_next_ = true;
}
};
#ifndef FMT_USE_FALLBACK_FILE
# define FMT_USE_FALLBACK_FILE 1
#endif
template <typename F,
FMT_ENABLE_IF(sizeof(F::_p) != 0 && !FMT_USE_FALLBACK_FILE)>
auto get_file(F* f, int) -> apple_file<F> {
return f;
}
template <typename F,
FMT_ENABLE_IF(sizeof(F::_IO_read_ptr) != 0 && !FMT_USE_FALLBACK_FILE)>
inline auto get_file(F* f, int) -> glibc_file<F> {
return f;
}
inline auto get_file(FILE* f, ...) -> fallback_file<FILE> { return f; }
using file_ref = decltype(get_file(static_cast<FILE*>(nullptr), 0));
template <typename F = FILE, typename Enable = void>
class file_print_buffer : public buffer<char> {
public:
explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {}
};
template <typename F>
class file_print_buffer<F, enable_if_t<has_flockfile<F>::value>>
: public buffer<char> {
private:
file_ref file_;
static void grow(buffer<char>& base, size_t) {
auto& self = static_cast<file_print_buffer&>(base);
self.file_.advance_write_buffer(self.size());
if (self.file_.get_write_buffer().size == 0) self.file_.flush();
auto buf = self.file_.get_write_buffer();
FMT_ASSERT(buf.size > 0, "");
self.set(buf.data, buf.size);
self.clear();
}
public:
explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) {
flockfile(f);
file_.init_buffer();
auto buf = file_.get_write_buffer();
set(buf.data, buf.size);
}
~file_print_buffer() {
file_.advance_write_buffer(size());
bool flush = file_.needs_flush();
F* f = file_; // Make funlockfile depend on the template parameter F
funlockfile(f); // for the system API detection to work.
if (flush) fflush(file_);
}
};
#if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE)
FMT_FUNC auto write_console(int, string_view) -> bool { return false; } FMT_FUNC auto write_console(int, string_view) -> bool { return false; }
FMT_FUNC auto write_console(std::FILE*, string_view) -> bool { return false; }
#else #else
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( //
@ -1445,23 +1683,21 @@ FMT_FUNC bool write_console(int fd, string_view text) {
return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(), return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
static_cast<dword>(u16.size()), nullptr, nullptr) != 0; static_cast<dword>(u16.size()), nullptr, nullptr) != 0;
} }
FMT_FUNC auto write_console(std::FILE* f, string_view text) -> bool {
return write_console(_fileno(f), text);
}
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding. // Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args,
bool newline) {
auto buffer = memory_buffer(); auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args); detail::vformat_to(buffer, fmt, args);
if (newline) buffer.push_back('\n');
fwrite_fully(buffer.data(), buffer.size(), f); fwrite_fully(buffer.data(), buffer.size(), f);
} }
#endif #endif
FMT_FUNC void print(std::FILE* f, string_view text) { FMT_FUNC void print(std::FILE* f, string_view text) {
#ifdef _WIN32 #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
int fd = _fileno(f); int fd = _fileno(f);
if (_isatty(fd)) { if (_isatty(fd)) {
std::fflush(f); std::fflush(f);
@ -1472,12 +1708,26 @@ FMT_FUNC void print(std::FILE* f, string_view text) {
} }
} // namespace detail } // namespace detail
FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer(); auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args); detail::vformat_to(buffer, fmt, args);
detail::print(f, {buffer.data(), buffer.size()}); detail::print(f, {buffer.data(), buffer.size()});
} }
FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>())
return vprint_buffered(f, fmt, args);
auto&& buffer = detail::file_print_buffer<>(f);
return detail::vformat_to(buffer, fmt, args);
}
FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
buffer.push_back('\n');
detail::print(f, {buffer.data(), buffer.size()});
}
FMT_FUNC void vprint(string_view fmt, format_args args) { FMT_FUNC void vprint(string_view fmt, format_args args) {
vprint(stdout, fmt, args); vprint(stdout, fmt, args);
} }

File diff suppressed because it is too large Load Diff

View File

@ -8,18 +8,18 @@
#ifndef FMT_OS_H_ #ifndef FMT_OS_H_
#define FMT_OS_H_ #define FMT_OS_H_
#include <cerrno>
#include <cstddef>
#include <cstdio>
#include <system_error> // std::system_error
#include "format.h" #include "format.h"
#if defined __APPLE__ || defined(__FreeBSD__) #ifndef FMT_MODULE
# include <cerrno>
# include <cstddef>
# include <cstdio>
# include <system_error> // std::system_error
# if FMT_HAS_INCLUDE(<xlocale.h>) # if FMT_HAS_INCLUDE(<xlocale.h>)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X # include <xlocale.h> // LC_NUMERIC_MASK on macOS
# endif # endif
#endif #endif // FMT_MODULE
#ifndef FMT_USE_FCNTL #ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe. // UWP doesn't provide _pipe.
@ -77,46 +77,33 @@ FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT FMT_BEGIN_EXPORT
/** /**
\rst * A reference to a null-terminated string. It can be constructed from a C
A reference to a null-terminated string. It can be constructed from a C * string or `std::string`.
string or ``std::string``. *
* You can use one of the following type aliases for common character types:
You can use one of the following type aliases for common character types: *
* +---------------+-----------------------------+
+---------------+-----------------------------+ * | Type | Definition |
| Type | Definition | * +===============+=============================+
+===============+=============================+ * | cstring_view | basic_cstring_view<char> |
| cstring_view | basic_cstring_view<char> | * +---------------+-----------------------------+
+---------------+-----------------------------+ * | wcstring_view | basic_cstring_view<wchar_t> |
| wcstring_view | basic_cstring_view<wchar_t> | * +---------------+-----------------------------+
+---------------+-----------------------------+ *
* This class is most useful as a parameter type for functions that wrap C APIs.
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/ */
template <typename Char> class basic_cstring_view { template <typename Char> class basic_cstring_view {
private: private:
const Char* data_; const Char* data_;
public: public:
/** Constructs a string reference object from a C string. */ /// Constructs a string reference object from a C string.
basic_cstring_view(const Char* s) : data_(s) {} basic_cstring_view(const Char* s) : data_(s) {}
/** /// Constructs a string reference from an `std::string` object.
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {} basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */ /// Returns the pointer to a C string.
auto c_str() const -> const Char* { return data_; } auto c_str() const -> const Char* { return data_; }
}; };
@ -135,33 +122,30 @@ FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
format_args args); format_args args);
/** /**
\rst * Constructs a `std::system_error` object with the description of the form
Constructs a :class:`std::system_error` object with the description *
of the form * <message>: <system-message>
*
.. parsed-literal:: * where `<message>` is the formatted message and `<system-message>` is the
*<message>*: *<system-message>* * system message corresponding to the error code.
* `error_code` is a Windows error code as given by `GetLastError`.
where *<message>* is the formatted message and *<system-message>* is the * If `error_code` is not a valid error code such as -1, the system message
system message corresponding to the error code. * will look like "error -1".
*error_code* is a Windows error code as given by ``GetLastError``. *
If *error_code* is not a valid error code such as -1, the system message * **Example**:
will look like "error -1". *
* // This throws a system_error with the description
**Example**:: * // cannot open file 'madeup': The system cannot find the file
* specified.
// This throws a system_error with the description * // or similar (system message may vary).
// cannot open file 'madeup': The system cannot find the file specified. * const char *filename = "madeup";
// or similar (system message may vary). * LPOFSTRUCT of = LPOFSTRUCT();
const char *filename = "madeup"; * HFILE file = OpenFile(filename, &of, OF_READ);
LPOFSTRUCT of = LPOFSTRUCT(); * if (file == HFILE_ERROR) {
HFILE file = OpenFile(filename, &of, OF_READ); * throw fmt::windows_error(GetLastError(),
if (file == HFILE_ERROR) { * "cannot open file '{}'", filename);
throw fmt::windows_error(GetLastError(), * }
"cannot open file '{}'", filename); */
}
\endrst
*/
template <typename... Args> template <typename... Args>
std::system_error windows_error(int error_code, string_view message, std::system_error windows_error(int error_code, string_view message,
const Args&... args) { const Args&... args) {
@ -227,17 +211,16 @@ class buffered_file {
FMT_API auto descriptor() const -> int; FMT_API auto descriptor() const -> int;
void vprint(string_view format_str, format_args args) { template <typename... T>
fmt::vprint(file_, format_str, args); inline void print(string_view fmt, const T&... args) {
} const auto& vargs = fmt::make_format_args(args...);
detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
template <typename... Args> : fmt::vprint(file_, fmt, vargs);
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, fmt::make_format_args(args...));
} }
}; };
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1. // A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with noexcept may throw // Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as // fmt::system_error in case of failure. Note that some errors such as
@ -251,6 +234,8 @@ class FMT_API file {
// Constructs a file object with a given descriptor. // Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {} explicit file(int fd) : fd_(fd) {}
friend struct pipe;
public: public:
// Possible values for the oflag argument to the constructor. // Possible values for the oflag argument to the constructor.
enum { enum {
@ -313,11 +298,6 @@ class FMT_API file {
// necessary. // necessary.
void dup2(int fd, std::error_code& ec) noexcept; void dup2(int fd, std::error_code& ec) noexcept;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
// DEPRECATED! Taking files as out parameters is deprecated.
static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // this file object from the file.
auto fdopen(const char* mode) -> buffered_file; auto fdopen(const char* mode) -> buffered_file;
@ -329,6 +309,15 @@ class FMT_API file {
# endif # endif
}; };
struct FMT_API pipe {
file read_end;
file write_end;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
pipe();
};
// Returns the memory page size. // Returns the memory page size.
auto getpagesize() -> long; auto getpagesize() -> long;
@ -370,13 +359,14 @@ struct ostream_params {
}; };
class file_buffer final : public buffer<char> { class file_buffer final : public buffer<char> {
private:
file file_; file file_;
FMT_API void grow(size_t) override; FMT_API static void grow(buffer<char>& buf, size_t);
public: public:
FMT_API file_buffer(cstring_view path, const ostream_params& params); FMT_API file_buffer(cstring_view path, const ostream_params& params);
FMT_API file_buffer(file_buffer&& other); FMT_API file_buffer(file_buffer&& other) noexcept;
FMT_API ~file_buffer(); FMT_API ~file_buffer();
void flush() { void flush() {
@ -393,11 +383,10 @@ class file_buffer final : public buffer<char> {
} // namespace detail } // namespace detail
// Added {} below to work around default constructor error known to constexpr auto buffer_size = detail::buffer_size();
// occur in Xcode versions 7.2.1 and 8.2.1.
constexpr detail::buffer_size buffer_size{};
/** A fast output stream which is not thread-safe. */ /// A fast output stream for writing from a single thread. Writing from
/// multiple threads without external synchronization may result in a data race.
class FMT_API ostream { class FMT_API ostream {
private: private:
FMT_MSC_WARNING(suppress : 4251) FMT_MSC_WARNING(suppress : 4251)
@ -418,30 +407,25 @@ class FMT_API ostream {
void close() { buffer_.close(); } void close() { buffer_.close(); }
/** /// Formats `args` according to specifications in `fmt` and writes the
Formats ``args`` according to specifications in ``fmt`` and writes the /// output to the file.
output to the file.
*/
template <typename... T> void print(format_string<T...> fmt, T&&... args) { template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(std::back_inserter(buffer_), fmt, vformat_to(appender(buffer_), fmt, fmt::make_format_args(args...));
fmt::make_format_args(args...));
} }
}; };
/** /**
\rst * Opens a file for writing. Supported parameters passed in `params`:
Opens a file for writing. Supported parameters passed in *params*: *
* - `<integer>`: Flags passed to [open](
* ``<integer>``: Flags passed to `open * https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html)
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ * (`file::WRONLY | file::CREATE | file::TRUNC` by default)
(``file::WRONLY | file::CREATE | file::TRUNC`` by default) * - `buffer_size=<integer>`: Output buffer size
* ``buffer_size=<integer>``: Output buffer size *
* **Example**:
**Example**:: *
* auto out = fmt::output_file("guide.txt");
auto out = fmt::output_file("guide.txt"); * out.print("Don't {}", "Panic");
out.print("Don't {}", "Panic");
\endrst
*/ */
template <typename... T> template <typename... T>
inline auto output_file(cstring_view path, T... params) -> ostream { inline auto output_file(cstring_view path, T... params) -> ostream {

View File

@ -8,7 +8,9 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include <fstream> // std::filebuf #ifndef FMT_MODULE
# include <fstream> // std::filebuf
#endif
#ifdef _WIN32 #ifdef _WIN32
# ifdef __GLIBCXX__ # ifdef __GLIBCXX__
@ -18,42 +20,11 @@
# include <io.h> # include <io.h>
#endif #endif
#include "format.h" #include "chrono.h" // formatbuf
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename Streambuf> class formatbuf : public Streambuf {
private:
using char_type = typename Streambuf::char_type;
using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
using int_type = typename Streambuf::int_type;
using traits_type = typename Streambuf::traits_type;
buffer<char_type>& buffer_;
public:
explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
protected:
// The put area is always empty. This makes the implementation simpler and has
// the advantage that the streambuf and the buffer are always in sync and
// sputc never writes into uninitialized memory. A disadvantage is that each
// call to sputc always results in a (virtual) call to overflow. There is no
// disadvantage here for sputn since this always results in a call to xsputn.
auto overflow(int_type ch) -> int_type override {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<char_type>(ch));
return ch;
}
auto xsputn(const char_type* s, streamsize count) -> streamsize override {
buffer_.append(s, s + count);
return count;
}
};
// 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 {
@ -73,12 +44,12 @@ auto get_file(std::filebuf&) -> FILE*;
inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data) inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
-> bool { -> bool {
FILE* f = nullptr; FILE* f = nullptr;
#if FMT_MSC_VERSION #if FMT_MSC_VERSION && FMT_USE_RTTI
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
f = get_file(*buf); f = get_file(*buf);
else else
return false; return false;
#elif defined(_WIN32) && defined(__GLIBCXX__) #elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
auto* rdbuf = os.rdbuf(); auto* rdbuf = os.rdbuf();
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
f = sfbuf->file(); f = sfbuf->file();
@ -143,9 +114,8 @@ 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; void set_debug_format() = delete;
template <typename T, typename OutputIt> template <typename T, typename Context>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
-> OutputIt {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
detail::format_value(buffer, value); detail::format_value(buffer, value);
return formatter<basic_string_view<Char>, Char>::format( return formatter<basic_string_view<Char>, Char>::format(
@ -158,22 +128,20 @@ using ostream_formatter = basic_ostream_formatter<char>;
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<detail::streamed_view<T>, Char> struct formatter<detail::streamed_view<T>, Char>
: basic_ostream_formatter<Char> { : basic_ostream_formatter<Char> {
template <typename OutputIt> template <typename Context>
auto format(detail::streamed_view<T> view, auto format(detail::streamed_view<T> view, Context& ctx) const
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { -> decltype(ctx.out()) {
return basic_ostream_formatter<Char>::format(view.value, ctx); return basic_ostream_formatter<Char>::format(view.value, ctx);
} }
}; };
/** /**
\rst * Returns a view that formats `value` via an ostream `operator<<`.
Returns a view that formats `value` via an ostream ``operator<<``. *
* **Example**:
**Example**:: *
* fmt::print("Current thread id: {}\n",
fmt::print("Current thread id: {}\n", * fmt::streamed(std::this_thread::get_id()));
fmt::streamed(std::this_thread::get_id()));
\endrst
*/ */
template <typename T> template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> { constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
@ -194,7 +162,7 @@ inline void vprint_directly(std::ostream& os, string_view format_str,
FMT_EXPORT template <typename Char> FMT_EXPORT template <typename Char>
void vprint(std::basic_ostream<Char>& os, void vprint(std::basic_ostream<Char>& os,
basic_string_view<type_identity_t<Char>> format_str, basic_string_view<type_identity_t<Char>> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { typename detail::vformat_args<Char>::type 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; if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
@ -202,18 +170,16 @@ void vprint(std::basic_ostream<Char>& os,
} }
/** /**
\rst * Prints formatted data to the stream `os`.
Prints formatted data to the stream *os*. *
* **Example**:
**Example**:: *
* fmt::print(cerr, "Don't {}!", "panic");
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/ */
FMT_EXPORT template <typename... T> FMT_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) {
const auto& vargs = fmt::make_format_args(args...); const auto& vargs = fmt::make_format_args(args...);
if (detail::is_utf8()) if (detail::use_utf8())
vprint(os, fmt, vargs); vprint(os, fmt, vargs);
else else
detail::vprint_directly(os, fmt, vargs); detail::vprint_directly(os, fmt, vargs);
@ -224,7 +190,7 @@ template <typename... Args>
void print(std::wostream& os, void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt, basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) { Args&&... args) {
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
} }
FMT_EXPORT template <typename... T> FMT_EXPORT template <typename... T>

View File

@ -8,8 +8,10 @@
#ifndef FMT_PRINTF_H_ #ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_ #define FMT_PRINTF_H_
#include <algorithm> // std::max #ifndef FMT_MODULE
#include <limits> // std::numeric_limits # include <algorithm> // std::max
# include <limits> // std::numeric_limits
#endif
#include "format.h" #include "format.h"
@ -22,7 +24,7 @@ template <typename T> struct printf_formatter {
template <typename Char> class basic_printf_context { template <typename Char> class basic_printf_context {
private: private:
detail::buffer_appender<Char> out_; basic_appender<Char> out_;
basic_format_args<basic_printf_context> args_; basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value || static_assert(std::is_same<Char, char>::value ||
@ -34,28 +36,20 @@ template <typename Char> class basic_printf_context {
using parse_context_type = basic_format_parse_context<Char>; using parse_context_type = basic_format_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>; template <typename T> using formatter_type = printf_formatter<T>;
/** /// Constructs a `printf_context` object. References to the arguments are
\rst /// stored in the context object so make sure they have appropriate lifetimes.
Constructs a ``printf_context`` object. References to the arguments are basic_printf_context(basic_appender<Char> out,
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(detail::buffer_appender<Char> out,
basic_format_args<basic_printf_context> args) basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {} : out_(out), args_(args) {}
auto out() -> detail::buffer_appender<Char> { return out_; } auto out() -> basic_appender<Char> { return out_; }
void advance_to(detail::buffer_appender<Char>) {} void advance_to(basic_appender<Char>) {}
auto locale() -> detail::locale_ref { return {}; } auto locale() -> detail::locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> { auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id); return args_.get(id);
} }
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
}
}; };
namespace detail { namespace detail {
@ -64,7 +58,7 @@ namespace detail {
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> struct int_checker { template <bool IsSigned> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool { template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = max_value<int>(); unsigned max = to_unsigned(max_value<int>());
return value <= max; return value <= max;
} }
static auto fits_in_int(bool) -> bool { return true; } static auto fits_in_int(bool) -> bool { return true; }
@ -82,13 +76,13 @@ struct printf_precision_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> int { auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
throw_format_error("number is too big"); report_error("number is too big");
return (std::max)(static_cast<int>(value), 0); return (std::max)(static_cast<int>(value), 0);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> int { auto operator()(T) -> int {
throw_format_error("precision is not integer"); report_error("precision is not integer");
return 0; return 0;
} }
}; };
@ -165,7 +159,7 @@ template <typename T, typename Context> class arg_converter {
// unsigned). // unsigned).
template <typename T, typename Context, typename Char> template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) { void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg); arg.visit(arg_converter<T, Context>(arg, type));
} }
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
@ -195,12 +189,12 @@ template <typename Char> struct get_cstring {
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
template <typename Char> class printf_width_handler { class printf_width_handler {
private: private:
format_specs<Char>& specs_; format_specs& specs_;
public: public:
explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {} explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> unsigned { auto operator()(T value) -> unsigned {
@ -209,14 +203,14 @@ template <typename Char> class printf_width_handler {
specs_.align = align::left; specs_.align = align::left;
width = 0 - width; width = 0 - width;
} }
unsigned int_max = max_value<int>(); unsigned int_max = to_unsigned(max_value<int>());
if (width > int_max) throw_format_error("number is too big"); if (width > int_max) report_error("number is too big");
return static_cast<unsigned>(width); return static_cast<unsigned>(width);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> unsigned { auto operator()(T) -> unsigned {
throw_format_error("width is not integer"); report_error("width is not integer");
return 0; return 0;
} }
}; };
@ -224,12 +218,12 @@ template <typename Char> class printf_width_handler {
// Workaround for a bug with the XL compiler when initializing // Workaround for a bug with the XL compiler when initializing
// printf_arg_formatter's base class. // printf_arg_formatter's base class.
template <typename Char> template <typename Char>
auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s) auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
-> arg_formatter<Char> { -> arg_formatter<Char> {
return {iter, s, locale_ref()}; return {iter, s, locale_ref()};
} }
// The ``printf`` argument formatter. // The `printf` argument formatter.
template <typename Char> template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> { class printf_arg_formatter : public arg_formatter<Char> {
private: private:
@ -241,11 +235,11 @@ class printf_arg_formatter : public arg_formatter<Char> {
void write_null_pointer(bool is_string = false) { void write_null_pointer(bool is_string = false) {
auto s = this->specs; auto s = this->specs;
s.type = presentation_type::none; s.type = presentation_type::none;
write_bytes(this->out, is_string ? "(null)" : "(nil)", s); write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
} }
public: public:
printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s, printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
context_type& ctx) context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {} : base(make_arg_formatter(iter, s)), context_(ctx) {}
@ -259,19 +253,18 @@ class printf_arg_formatter : public arg_formatter<Char> {
base::operator()(value); base::operator()(value);
return; return;
} }
format_specs<Char> fmt_specs = this->specs; format_specs s = this->specs;
if (fmt_specs.type != presentation_type::none && if (s.type != presentation_type::none && s.type != presentation_type::chr) {
fmt_specs.type != presentation_type::chr) {
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
} }
fmt_specs.sign = sign::none; s.sign = sign::none;
fmt_specs.alt = false; s.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. s.fill = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is // align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types // ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) if (s.align == align::none || s.align == align::numeric)
fmt_specs.align = align::right; s.align = align::right;
write<Char>(this->out, static_cast<Char>(value), fmt_specs); write<Char>(this->out, static_cast<Char>(value), s);
} }
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
@ -279,7 +272,6 @@ class printf_arg_formatter : public arg_formatter<Char> {
base::operator()(value); base::operator()(value);
} }
/** Formats a null-terminated C string. */
void operator()(const char* value) { void operator()(const char* value) {
if (value) if (value)
base::operator()(value); base::operator()(value);
@ -287,7 +279,6 @@ class printf_arg_formatter : public arg_formatter<Char> {
write_null_pointer(this->specs.type != presentation_type::pointer); write_null_pointer(this->specs.type != presentation_type::pointer);
} }
/** Formats a null-terminated wide C string. */
void operator()(const wchar_t* value) { void operator()(const wchar_t* value) {
if (value) if (value)
base::operator()(value); base::operator()(value);
@ -297,7 +288,6 @@ class printf_arg_formatter : public arg_formatter<Char> {
void operator()(basic_string_view<Char> value) { base::operator()(value); } void operator()(basic_string_view<Char> value) { base::operator()(value); }
/** Formats a pointer. */
void operator()(const void* value) { void operator()(const void* value) {
if (value) if (value)
base::operator()(value); base::operator()(value);
@ -305,7 +295,6 @@ class printf_arg_formatter : public arg_formatter<Char> {
write_null_pointer(); write_null_pointer();
} }
/** Formats an argument of a custom (user-defined) type. */
void operator()(typename basic_format_arg<context_type>::handle handle) { void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = basic_format_parse_context<Char>({}); auto parse_ctx = basic_format_parse_context<Char>({});
handle.format(parse_ctx, context_); handle.format(parse_ctx, context_);
@ -313,7 +302,7 @@ class printf_arg_formatter : public arg_formatter<Char> {
}; };
template <typename Char> template <typename Char>
void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) { void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
for (; it != end; ++it) { for (; it != end; ++it) {
switch (*it) { switch (*it) {
case '-': case '-':
@ -323,7 +312,7 @@ void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
specs.sign = sign::plus; specs.sign = sign::plus;
break; break;
case '0': case '0':
specs.fill[0] = '0'; specs.fill = '0';
break; break;
case ' ': case ' ':
if (specs.sign != sign::plus) specs.sign = sign::space; if (specs.sign != sign::plus) specs.sign = sign::space;
@ -338,7 +327,7 @@ void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
} }
template <typename Char, typename GetArg> template <typename Char, typename GetArg>
auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs, auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int { GetArg get_arg) -> int {
int arg_index = -1; int arg_index = -1;
Char c = *it; Char c = *it;
@ -350,11 +339,11 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
++it; ++it;
arg_index = value != -1 ? value : max_value<int>(); arg_index = value != -1 ? value : max_value<int>();
} else { } else {
if (c == '0') specs.fill[0] = '0'; if (c == '0') specs.fill = '0';
if (value != 0) { if (value != 0) {
// Nonzero value means that we parsed width and don't need to // Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now. // parse it or flags again, so return now.
if (value == -1) throw_format_error("number is too big"); if (value == -1) report_error("number is too big");
specs.width = value; specs.width = value;
return arg_index; return arg_index;
} }
@ -365,17 +354,17 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
if (it != end) { if (it != end) {
if (*it >= '0' && *it <= '9') { if (*it >= '0' && *it <= '9') {
specs.width = parse_nonnegative_int(it, end, -1); specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) throw_format_error("number is too big"); if (specs.width == -1) report_error("number is too big");
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
specs.width = static_cast<int>(visit_format_arg( specs.width = static_cast<int>(
detail::printf_width_handler<Char>(specs), get_arg(-1))); get_arg(-1).visit(detail::printf_width_handler(specs)));
} }
} }
return arg_index; return arg_index;
} }
inline auto parse_printf_presentation_type(char c, type t) inline auto parse_printf_presentation_type(char c, type t, bool& upper)
-> presentation_type { -> presentation_type {
using pt = presentation_type; using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
@ -384,26 +373,31 @@ inline auto parse_printf_presentation_type(char c, type t)
return in(t, integral_set) ? pt::dec : pt::none; return in(t, integral_set) ? pt::dec : pt::none;
case 'o': case 'o':
return in(t, integral_set) ? pt::oct : pt::none; return in(t, integral_set) ? pt::oct : pt::none;
case 'x':
return in(t, integral_set) ? pt::hex_lower : pt::none;
case 'X': case 'X':
return in(t, integral_set) ? pt::hex_upper : pt::none; upper = true;
case 'a': FMT_FALLTHROUGH;
return in(t, float_set) ? pt::hexfloat_lower : pt::none; case 'x':
case 'A': return in(t, integral_set) ? pt::hex : pt::none;
return in(t, float_set) ? pt::hexfloat_upper : pt::none;
case 'e':
return in(t, float_set) ? pt::exp_lower : pt::none;
case 'E': case 'E':
return in(t, float_set) ? pt::exp_upper : pt::none; upper = true;
case 'f': FMT_FALLTHROUGH;
return in(t, float_set) ? pt::fixed_lower : pt::none; case 'e':
return in(t, float_set) ? pt::exp : pt::none;
case 'F': case 'F':
return in(t, float_set) ? pt::fixed_upper : pt::none; upper = true;
case 'g': FMT_FALLTHROUGH;
return in(t, float_set) ? pt::general_lower : pt::none; case 'f':
return in(t, float_set) ? pt::fixed : pt::none;
case 'G': case 'G':
return in(t, float_set) ? pt::general_upper : pt::none; upper = true;
FMT_FALLTHROUGH;
case 'g':
return in(t, float_set) ? pt::general : pt::none;
case 'A':
upper = true;
FMT_FALLTHROUGH;
case 'a':
return in(t, float_set) ? pt::hexfloat : pt::none;
case 'c': case 'c':
return in(t, integral_set) ? pt::chr : pt::none; return in(t, integral_set) ? pt::chr : pt::none;
case 's': case 's':
@ -418,7 +412,7 @@ inline auto parse_printf_presentation_type(char c, type t)
template <typename Char, typename Context> template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format, void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) { basic_format_args<Context> args) {
using iterator = buffer_appender<Char>; using iterator = basic_appender<Char>;
auto out = iterator(buf); auto out = iterator(buf);
auto context = basic_printf_context<Char>(out, args); auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = basic_format_parse_context<Char>(format); auto parse_ctx = basic_format_parse_context<Char>(format);
@ -449,12 +443,12 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
} }
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
auto specs = format_specs<Char>(); auto specs = format_specs();
specs.align = align::right; specs.align = align::right;
// Parse argument index, flags and width. // Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg); int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) throw_format_error("argument not found"); if (arg_index == 0) report_error("argument not found");
// Parse precision. // Parse precision.
if (it != end && *it == '.') { if (it != end && *it == '.') {
@ -464,8 +458,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
specs.precision = parse_nonnegative_int(it, end, 0); specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') { } else if (c == '*') {
++it; ++it;
specs.precision = static_cast<int>( specs.precision =
visit_format_arg(printf_precision_handler(), get_arg(-1))); static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
} else { } else {
specs.precision = 0; specs.precision = 0;
} }
@ -476,22 +470,22 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
// specified, the '0' flag is ignored // specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral()) { if (specs.precision >= 0 && arg.is_integral()) {
// Ignore '0' for non-numeric types or if '-' present. // Ignore '0' for non-numeric types or if '-' present.
specs.fill[0] = ' '; specs.fill = ' ';
} }
if (specs.precision >= 0 && arg.type() == type::cstring_type) { if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = visit_format_arg(get_cstring<Char>(), arg); auto str = arg.visit(get_cstring<Char>());
auto str_end = str + specs.precision; auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char()); auto nul = std::find(str, str_end, Char());
auto sv = basic_string_view<Char>( auto sv = basic_string_view<Char>(
str, to_unsigned(nul != str_end ? nul - str : specs.precision)); str, to_unsigned(nul != str_end ? nul - str : specs.precision));
arg = make_arg<basic_printf_context<Char>>(sv); arg = make_arg<basic_printf_context<Char>>(sv);
} }
if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false; if (specs.alt && arg.visit(is_zero_int())) specs.alt = false;
if (specs.fill[0] == '0') { if (specs.fill.template get<Char>() == '0') {
if (arg.is_arithmetic() && specs.align != align::left) if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric; specs.align = align::numeric;
else else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' specs.fill = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present. // flag is also present.
} }
@ -536,7 +530,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
} }
// Parse type. // Parse type.
if (it == end) throw_format_error("invalid format string"); if (it == end) report_error("invalid format string");
char type = static_cast<char>(*it++); char type = static_cast<char>(*it++);
if (arg.is_integral()) { if (arg.is_integral()) {
// Normalize type. // Normalize type.
@ -546,18 +540,20 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
type = 'd'; type = 'd';
break; break;
case 'c': case 'c':
visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg); arg.visit(char_converter<basic_printf_context<Char>>(arg));
break; break;
} }
} }
specs.type = parse_printf_presentation_type(type, arg.type()); bool upper = false;
specs.type = parse_printf_presentation_type(type, arg.type(), upper);
if (specs.type == presentation_type::none) if (specs.type == presentation_type::none)
throw_format_error("invalid format specifier"); report_error("invalid format specifier");
specs.upper = upper;
start = it; start = it;
// Format argument. // Format argument.
visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg); arg.visit(printf_arg_formatter<Char>(out, specs, context));
} }
write(out, basic_string_view<Char>(start, to_unsigned(it - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
} }
@ -569,29 +565,21 @@ using wprintf_context = basic_printf_context<wchar_t>;
using printf_args = basic_format_args<printf_context>; using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>; using wprintf_args = basic_format_args<wprintf_context>;
/** /// Constructs an `format_arg_store` object that contains references to
\rst /// arguments and can be implicitly converted to `printf_args`.
Constructs an `~fmt::format_arg_store` object that contains references to template <typename Char = char, typename... T>
arguments and can be implicitly converted to `~fmt::printf_args`. inline auto make_printf_args(T&... args)
\endrst -> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
*/ return fmt::make_format_args<basic_printf_context<Char>>(args...);
template <typename... T>
inline auto make_printf_args(const T&... args)
-> format_arg_store<printf_context, T...> {
return {args...};
} }
// DEPRECATED! template <typename Char> struct vprintf_args {
template <typename... T> using type = basic_format_args<basic_printf_context<Char>>;
inline auto make_wprintf_args(const T&... args) };
-> format_arg_store<wprintf_context, T...> {
return {args...};
}
template <typename Char> template <typename Char>
inline auto vsprintf( inline auto vsprintf(basic_string_view<Char> fmt,
basic_string_view<Char> fmt, typename vprintf_args<Char>::type args)
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args); detail::vprintf(buf, fmt, args);
@ -599,26 +587,22 @@ inline auto vsprintf(
} }
/** /**
\rst * Formats `args` according to specifications in `fmt` and returns the result
Formats arguments and returns the result as a string. * as as string.
*
**Example**:: * **Example**:
*
std::string message = fmt::sprintf("The answer is %d", 42); * std::string message = fmt::sprintf("The answer is %d", 42);
\endrst */
*/ template <typename S, typename... T, typename Char = char_t<S>>
template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt), return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...)); fmt::make_format_args<basic_printf_context<Char>>(args...));
} }
template <typename Char> template <typename Char>
inline auto vfprintf( inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
std::FILE* f, basic_string_view<Char> fmt, typename vprintf_args<Char>::type args) -> int {
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> int {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args); detail::vprintf(buf, fmt, args);
size_t size = buf.size(); size_t size = buf.size();
@ -628,36 +612,33 @@ inline auto vfprintf(
} }
/** /**
\rst * Formats `args` according to specifications in `fmt` and writes the output
Prints formatted data to the file *f*. * to `f`.
*
**Example**:: * **Example**:
*
fmt::fprintf(stderr, "Don't %s!", "panic"); * fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/ */
template <typename S, typename... T, typename Char = char_t<S>> template <typename S, typename... T, typename Char = char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt), return vfprintf(f, detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...)); make_printf_args<Char>(args...));
} }
template <typename Char> template <typename Char>
FMT_DEPRECATED inline auto vprintf( FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
basic_string_view<Char> fmt, typename vprintf_args<Char>::type args)
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> int { -> int {
return vfprintf(stdout, fmt, args); return vfprintf(stdout, fmt, args);
} }
/** /**
\rst * Formats `args` according to specifications in `fmt` and writes the output
Prints formatted data to ``stdout``. * to `stdout`.
*
**Example**:: * **Example**:
*
fmt::printf("Elapsed time: %.2f seconds", 1.23); * fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/ */
template <typename... T> template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int { inline auto printf(string_view fmt, const T&... args) -> int {
@ -666,7 +647,7 @@ inline auto printf(string_view fmt, const T&... args) -> int {
template <typename... T> template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt, FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int { const T&... args) -> int {
return vfprintf(stdout, fmt, make_wprintf_args(args...)); return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
} }
FMT_END_EXPORT FMT_END_EXPORT

View File

@ -8,67 +8,31 @@
#ifndef FMT_RANGES_H_ #ifndef FMT_RANGES_H_
#define FMT_RANGES_H_ #define FMT_RANGES_H_
#include <initializer_list> #ifndef FMT_MODULE
#include <tuple> # include <initializer_list>
#include <type_traits> # include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
#endif
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_EXPORT
enum class range_format { disabled, map, set, sequence, string, debug_string };
namespace detail { namespace detail {
template <typename Range, typename OutputIt>
auto copy(const Range& range, OutputIt out) -> OutputIt {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIt>
auto copy(const char* str, OutputIt out) -> OutputIt {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
*out++ = ch;
return out;
}
template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
*out++ = ch;
return out;
}
// Returns true if T has a std::string-like interface, like std::string_view.
template <typename T> class is_std_string_like {
template <typename U>
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static constexpr const bool value =
is_string<T>::value ||
std::is_convertible<T, std_string_view<char>>::value ||
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Char>
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
template <typename T> class is_map { template <typename T> class is_map {
template <typename U> static auto check(U*) -> typename U::mapped_type; template <typename U> static auto check(U*) -> typename U::mapped_type;
template <typename> static void check(...); template <typename> static void check(...);
public: public:
#ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED!
static constexpr const bool value = false;
#else
static constexpr const bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
#endif
}; };
template <typename T> class is_set { template <typename T> class is_set {
@ -76,12 +40,8 @@ template <typename T> class is_set {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
#ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED!
static constexpr const bool value = false;
#else
static constexpr const bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
#endif
}; };
template <typename... Ts> struct conditional_helper {}; template <typename... Ts> struct conditional_helper {};
@ -110,17 +70,17 @@ template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {}; struct has_member_fn_begin_end_t : std::false_type {};
template <typename T> template <typename T>
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()), struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
decltype(std::declval<T>().end())>> decltype(std::declval<T>().end())>>
: std::true_type {}; : std::true_type {};
// Member function overload // Member function overloads.
template <typename T> template <typename T>
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin()); auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
template <typename T> template <typename T>
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end()); auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
// ADL overload. Only participates in overload resolution if member functions // ADL overloads. Only participate in overload resolution if member functions
// are not found. // are not found.
template <typename T> template <typename T>
auto range_begin(T&& rng) auto range_begin(T&& rng)
@ -141,16 +101,16 @@ struct has_mutable_begin_end : std::false_type {};
template <typename T> template <typename T>
struct has_const_begin_end< struct has_const_begin_end<
T, T, void_t<decltype(*detail::range_begin(
void_t< std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())), decltype(detail::range_end(
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>> std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {}; : std::true_type {};
template <typename T> template <typename T>
struct has_mutable_begin_end< struct has_mutable_begin_end<
T, void_t<decltype(detail::range_begin(std::declval<T>())), T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
decltype(detail::range_end(std::declval<T>())), decltype(detail::range_end(std::declval<T&>())),
// the extra int here is because older versions of MSVC don't // the extra int here is because older versions of MSVC don't
// SFINAE properly unless there are distinct types // SFINAE properly unless there are distinct types
int>> : std::true_type {}; int>> : std::true_type {};
@ -206,12 +166,13 @@ class is_tuple_formattable_ {
static constexpr const bool value = false; static constexpr const bool value = false;
}; };
template <typename T, typename C> class is_tuple_formattable_<T, C, true> { template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <std::size_t... Is> template <size_t... Is>
static auto check2(index_sequence<Is...>, static auto all_true(index_sequence<Is...>,
integer_sequence<bool, (Is == Is)...>) -> std::true_type; integer_sequence<bool, (Is >= 0)...>) -> std::true_type;
static auto check2(...) -> std::false_type; static auto all_true(...) -> std::false_type;
template <std::size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(check2( template <size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(all_true(
index_sequence<Is...>{}, index_sequence<Is...>{},
integer_sequence<bool, integer_sequence<bool,
(is_formattable<typename std::tuple_element<Is, T>::type, (is_formattable<typename std::tuple_element<Is, T>::type,
@ -292,6 +253,18 @@ FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
template <typename Formatter> template <typename Formatter>
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
std::is_same<uncvref_type<T>, T>::value
? range_format::disabled
: is_map<T>::value ? range_format::map
: is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K>
using range_format_constant = std::integral_constant<range_format, K>;
// These are not generic lambdas for compatibility with C++11. // These are not generic lambdas for compatibility with C++11.
template <typename ParseContext> struct parse_empty_specs { template <typename ParseContext> struct parse_empty_specs {
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) { template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
@ -305,8 +278,7 @@ template <typename FormatContext> struct format_tuple_element {
template <typename T> template <typename T>
void operator()(const formatter<T, char_type>& f, const T& v) { void operator()(const formatter<T, char_type>& f, const T& v) {
if (i > 0) if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
ctx.advance_to(f.format(v, ctx)); ctx.advance_to(f.format(v, ctx));
++i; ++i;
} }
@ -358,8 +330,7 @@ struct formatter<Tuple, Char,
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();
if (it != ctx.end() && *it != '}') if (it != ctx.end() && *it != '}') report_error("invalid format specifier");
FMT_THROW(format_error("invalid format specifier"));
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx}); detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
return it; return it;
} }
@ -367,19 +338,17 @@ struct formatter<Tuple, Char,
template <typename FormatContext> template <typename FormatContext>
auto format(const Tuple& value, FormatContext& ctx) const auto format(const Tuple& value, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out())); ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
detail::for_each2( detail::for_each2(
formatters_, value, formatters_, value,
detail::format_tuple_element<FormatContext>{0, ctx, separator_}); detail::format_tuple_element<FormatContext>{0, ctx, separator_});
return detail::copy_str<Char>(closing_bracket_, ctx.out()); return detail::copy<Char>(closing_bracket_, ctx.out());
} }
}; };
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::has_to_string_view<T>::value;
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_convertible<T, detail::std_string_view<Char>>::value;
}; };
namespace detail { namespace detail {
@ -401,8 +370,8 @@ template <typename Context> struct range_mapper {
template <typename Char, typename Element> template <typename Char, typename Element>
using range_formatter_type = using range_formatter_type =
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map( formatter<remove_cvref_t<decltype(range_mapper<buffered_context<Char>>{}
std::declval<Element>()))>, .map(std::declval<Element>()))>,
Char>; Char>;
template <typename R> template <typename R>
@ -438,6 +407,24 @@ struct range_formatter<
detail::string_literal<Char, '['>{}; detail::string_literal<Char, '['>{};
basic_string_view<Char> closing_bracket_ = basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ']'>{}; detail::string_literal<Char, ']'>{};
bool is_debug = false;
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
auto buf = basic_memory_buffer<Char>();
for (; it != end; ++it) buf.push_back(*it);
auto specs = format_specs();
specs.type = presentation_type::debug;
return detail::write<Char>(
out, basic_string_view<Char>(buf.data(), buf.size()), specs);
}
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It, Sentinel) const -> Output {
return out;
}
public: public:
FMT_CONSTEXPR range_formatter() {} FMT_CONSTEXPR range_formatter() {}
@ -460,17 +447,37 @@ struct range_formatter<
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();
detail::maybe_set_debug_format(underlying_, true);
if (it == end) return underlying_.parse(ctx);
if (it != end && *it == 'n') { switch (detail::to_ascii(*it)) {
case 'n':
set_brackets({}, {}); set_brackets({}, {});
++it; ++it;
break;
case '?':
is_debug = true;
set_brackets({}, {});
++it;
if (it == end || *it != 's') report_error("invalid format specifier");
FMT_FALLTHROUGH;
case 's':
if (!std::is_same<T, Char>::value)
report_error("invalid format specifier");
if (!is_debug) {
set_brackets(detail::string_literal<Char, '"'>{},
detail::string_literal<Char, '"'>{});
set_separator({});
detail::maybe_set_debug_format(underlying_, false);
}
++it;
return it;
} }
if (it != end && *it != '}') { if (it != end && *it != '}') {
if (*it != ':') FMT_THROW(format_error("invalid format specifier")); if (*it != ':') report_error("invalid format specifier");
detail::maybe_set_debug_format(underlying_, false);
++it; ++it;
} else {
detail::maybe_set_debug_format(underlying_, true);
} }
ctx.advance_to(it); ctx.advance_to(it);
@ -479,80 +486,27 @@ struct range_formatter<
template <typename R, typename FormatContext> template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
detail::range_mapper<buffer_context<Char>> mapper; auto mapper = detail::range_mapper<buffered_context<Char>>();
auto out = ctx.out(); auto out = ctx.out();
out = detail::copy_str<Char>(opening_bracket_, out);
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);
if (is_debug) return write_debug_string(out, std::move(it), end);
out = detail::copy<Char>(opening_bracket_, out);
int i = 0;
for (; it != end; ++it) { for (; it != end; ++it) {
if (i > 0) out = detail::copy_str<Char>(separator_, out); if (i > 0) out = detail::copy<Char>(separator_, out);
ctx.advance_to(out); ctx.advance_to(out);
auto&& item = *it; auto&& item = *it; // Need an lvalue
out = underlying_.format(mapper.map(item), ctx); out = underlying_.format(mapper.map(item), ctx);
++i; ++i;
} }
out = detail::copy_str<Char>(closing_bracket_, out); out = detail::copy<Char>(closing_bracket_, out);
return out; return out;
} }
}; };
enum class range_format { disabled, map, set, sequence, string, debug_string }; FMT_EXPORT
namespace detail {
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
std::is_same<uncvref_type<T>, T>::value
? range_format::disabled
: is_map<T>::value ? range_format::map
: is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K, typename R, typename Char, typename Enable = void>
struct range_default_formatter;
template <range_format K>
using range_format_constant = std::integral_constant<range_format, K>;
template <range_format K, typename R, typename Char>
struct range_default_formatter<
K, R, Char,
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, '}'>{});
}
FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
underlying_.underlying().set_brackets({}, {});
underlying_.underlying().set_separator(
detail::string_literal<Char, ':', ' '>{});
}
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> template <typename T, typename Char, typename Enable = void>
struct range_format_kind struct range_format_kind
: conditional_t< : conditional_t<
@ -562,18 +516,221 @@ struct range_format_kind
template <typename R, typename Char> template <typename R, typename Char>
struct formatter< struct formatter<
R, Char, R, Char,
enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value != enable_if_t<conjunction<
range_format::disabled> bool_constant<
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. // Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
, ,
detail::is_formattable_delayed<R, Char> detail::is_formattable_delayed<R, Char>
#endif #endif
>::value>> >::value>> {
: detail::range_default_formatter<range_format_kind<R, Char>::value, R, private:
Char> { using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
public:
using nonlocking = void;
FMT_CONSTEXPR formatter() {
if (detail::const_check(range_format_kind<R, Char>::value !=
range_format::set))
return;
range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return range_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return range_formatter_.format(range, ctx);
}
}; };
// A map formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::map>> {
private:
using map_type = detail::maybe_const_range<R>;
using element_type = detail::uncvref_type<map_type>;
decltype(detail::tuple::get_formatters<element_type, Char>(
detail::tuple_index_sequence<element_type>())) formatters_;
bool no_delimiters_ = false;
public:
FMT_CONSTEXPR formatter() {}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it != end) {
if (detail::to_ascii(*it) == 'n') {
no_delimiters_ = true;
++it;
}
if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
++it;
}
ctx.advance_to(it);
}
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
return it;
}
template <typename FormatContext>
auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out();
basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
if (!no_delimiters_) out = detail::copy<Char>(open, out);
int i = 0;
auto mapper = detail::range_mapper<buffered_context<Char>>();
basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{};
for (auto&& value : map) {
if (i > 0) out = detail::copy<Char>(sep, out);
ctx.advance_to(out);
detail::for_each2(formatters_, mapper.map(value),
detail::format_tuple_element<FormatContext>{
0, ctx, detail::string_literal<Char, ':', ' '>{}});
++i;
}
basic_string_view<Char> close = detail::string_literal<Char, '}'>{};
if (!no_delimiters_) out = detail::copy<Char>(close, out);
return out;
}
};
// A (debug_)string formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
range_format_kind<R, Char>::value ==
range_format::debug_string>> {
private:
using range_type = detail::maybe_const_range<R>;
using string_type =
conditional_t<std::is_constructible<
detail::std_string_view<Char>,
decltype(detail::range_begin(std::declval<R>())),
decltype(detail::range_end(std::declval<R>()))>::value,
detail::std_string_view<Char>, std::basic_string<Char>>;
formatter<string_type, Char> underlying_;
public:
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()) {
auto out = ctx.out();
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
out = underlying_.format(
string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
return out;
}
};
template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
It begin;
Sentinel end;
basic_string_view<Char> sep;
join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(std::move(b)), end(e), sep(s) {}
};
template <typename It, typename Sentinel, typename Char>
struct formatter<join_view<It, Sentinel, Char>, Char> {
private:
using value_type =
#ifdef __cpp_lib_ranges
std::iter_value_t<It>;
#else
typename std::iterator_traits<It>::value_type;
#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>&&>;
public:
using nonlocking = void;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* {
return value_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(view_ref& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto it = std::forward<view_ref>(value).begin;
auto out = ctx.out();
if (it == value.end) return out;
out = value_formatter_.format(*it, ctx);
++it;
while (it != value.end) {
out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
out = value_formatter_.format(*it, ctx);
++it;
}
return out;
}
};
/// 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 { template <typename Char, typename... T> struct tuple_join_view : detail::view {
const std::tuple<T...>& tuple; const std::tuple<T...>& tuple;
basic_string_view<Char> sep; basic_string_view<Char> sep;
@ -623,7 +780,7 @@ struct formatter<tuple_join_view<Char, T...>, Char> {
if (N > 1) { if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>()); auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1) if (end != end1)
FMT_THROW(format_error("incompatible format specs for tuple elements")); report_error("incompatible format specs for tuple elements");
} }
#endif #endif
return end; return end;
@ -642,13 +799,11 @@ struct formatter<tuple_join_view<Char, T...>, Char> {
typename FormatContext::iterator { typename FormatContext::iterator {
auto out = std::get<sizeof...(T) - N>(formatters_) auto out = std::get<sizeof...(T) - N>(formatters_)
.format(std::get<sizeof...(T) - N>(value.tuple), ctx); .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
if (N > 1) { if (N <= 1) return out;
out = std::copy(value.sep.begin(), value.sep.end(), out); out = detail::copy<Char>(value.sep, out);
ctx.advance_to(out); ctx.advance_to(out);
return do_format(value, ctx, std::integral_constant<size_t, N - 1>()); return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
} }
return out;
}
}; };
namespace detail { namespace detail {
@ -692,15 +847,13 @@ struct formatter<
FMT_BEGIN_EXPORT FMT_BEGIN_EXPORT
/** /**
\rst * Returns an object that formats `std::tuple` with elements separated by `sep`.
Returns an object that formats `tuple` with elements separated by `sep`. *
* **Example**:
**Example**:: *
* auto t = std::tuple<int, char>{1, 'a'};
std::tuple<int, char> t = {1, 'a'}; * fmt::print("{}", fmt::join(t, ", "));
fmt::print("{}", fmt::join(t, ", ")); * // Output: 1, a
// Output: "1, a"
\endrst
*/ */
template <typename... T> template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep) FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
@ -708,23 +861,14 @@ FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
return {tuple, sep}; return {tuple, sep};
} }
template <typename... T>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep};
}
/** /**
\rst * Returns an object that formats `std::initializer_list` with elements
Returns an object that formats `initializer_list` with elements separated by * separated by `sep`.
`sep`. *
* **Example**:
**Example**:: *
* fmt::print("{}", fmt::join({1, 2, 3}, ", "));
fmt::print("{}", fmt::join({1, 2, 3}, ", ")); * // Output: "1, 2, 3"
// Output: "1, 2, 3"
\endrst
*/ */
template <typename T> template <typename T>
auto join(std::initializer_list<T> list, string_view sep) auto join(std::initializer_list<T> list, string_view sep)

View File

@ -8,25 +8,24 @@
#ifndef FMT_STD_H_ #ifndef FMT_STD_H_
#define FMT_STD_H_ #define FMT_STD_H_
#include <atomic>
#include <bitset>
#include <cstdlib>
#include <exception>
#include <memory>
#include <thread>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>
#include "format.h" #include "format.h"
#include "ostream.h" #include "ostream.h"
#if FMT_HAS_INCLUDE(<version>) #ifndef FMT_MODULE
# include <version> # include <atomic>
#endif # include <bitset>
// Checking FMT_CPLUSPLUS for warning suppression in MSVC. # include <complex>
#if FMT_CPLUSPLUS >= 201703L # include <cstdlib>
# include <exception>
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>) # if FMT_HAS_INCLUDE(<filesystem>)
# include <filesystem> # include <filesystem>
# endif # endif
@ -36,10 +35,19 @@
# if FMT_HAS_INCLUDE(<optional>) # if FMT_HAS_INCLUDE(<optional>)
# include <optional> # include <optional>
# endif # endif
#endif # endif
// Use > instead of >= in the version check because <source_location> may be
#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>) // available after C++17 but before C++20 is marked as implemented.
# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location> # include <source_location>
# endif
# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>)
# include <expected>
# endif
#endif // FMT_MODULE
#if FMT_HAS_INCLUDE(<version>)
# include <version>
#endif #endif
// GCC 4 does not support FMT_HAS_INCLUDE. // GCC 4 does not support FMT_HAS_INCLUDE.
@ -52,17 +60,6 @@
# endif # endif
#endif #endif
// Check if typeid is available.
#ifndef FMT_USE_TYPEID
// __RTTI is for EDG compilers. In MSVC typeid is available without RTTI.
# if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \
defined(__INTEL_RTTI__) || defined(__RTTI)
# define FMT_USE_TYPEID 1
# else
# define FMT_USE_TYPEID 0
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined. // For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM #ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem # ifdef __cpp_lib_filesystem
@ -117,7 +114,7 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
FMT_EXPORT FMT_EXPORT
template <typename Char> struct formatter<std::filesystem::path, Char> { template <typename Char> struct formatter<std::filesystem::path, Char> {
private: private:
format_specs<Char> specs_; format_specs specs_;
detail::arg_ref<Char> width_ref_; detail::arg_ref<Char> width_ref_;
bool debug_ = false; bool debug_ = false;
char path_type_ = 0; char path_type_ = 0;
@ -137,18 +134,16 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
debug_ = true; debug_ = true;
++it; ++it;
} }
if (it != end && (*it == 'g')) path_type_ = *it++; if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++);
return it; return it;
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const { auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_; auto specs = specs_;
# ifdef _WIN32 auto path_string =
auto path_string = !path_type_ ? p.native() : p.generic_wstring(); !path_type_ ? p.native()
# else : p.generic_string<std::filesystem::path::value_type>();
auto path_string = !path_type_ ? p.native() : p.generic_string();
# endif
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_, detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx); ctx);
@ -163,6 +158,22 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
specs); specs);
} }
}; };
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM #endif // FMT_CPP_LIB_FILESYSTEM
@ -242,6 +253,56 @@ struct formatter<std::optional<T>, Char,
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // __cpp_lib_optional #endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<is_formattable<T, Char>::value &&
is_formattable<E, Char>::value>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::expected<T, E>& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (value.has_value()) {
out = detail::write<Char>(out, "expected(");
out = detail::write_escaped_alternative<Char>(out, *value);
} else {
out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error());
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location #ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_EXPORT FMT_EXPORT
@ -291,16 +352,6 @@ template <typename T, typename C> class is_variant_formattable_ {
decltype(check(variant_index_sequence<T>{}))::value; decltype(check(variant_index_sequence<T>{}))::value;
}; };
template <typename Char, typename OutputIt, typename T>
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (is_string<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
else if constexpr (std::is_same_v<T, Char>)
return write_escaped_char(out, v);
else
return write<Char>(out, v);
}
} // namespace detail } // namespace detail
template <typename T> struct is_variant_like { template <typename T> struct is_variant_like {
@ -346,7 +397,7 @@ struct formatter<
FMT_TRY { FMT_TRY {
std::visit( std::visit(
[&](const auto& v) { [&](const auto& v) {
out = detail::write_variant_alternative<Char>(out, v); out = detail::write_escaped_alternative<Char>(out, v);
}, },
value); value);
} }
@ -372,44 +423,18 @@ template <typename Char> struct formatter<std::error_code, Char> {
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(), format_specs<Char>()); out = detail::write_bytes<Char>(out, ec.category().name(), format_specs());
out = detail::write<Char>(out, Char(':')); out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value()); out = detail::write<Char>(out, ec.value());
return out; return out;
} }
}; };
FMT_EXPORT #if FMT_USE_RTTI
template <typename T, typename Char> namespace detail {
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public: template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
-> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = FMT_USE_TYPEID != 0;
}
return it;
}
template <typename OutputIt>
auto format(const std::exception& ex,
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
format_specs<Char> spec;
auto out = ctx.out();
if (!with_typename_)
return detail::write_bytes(out, string_view(ex.what()), spec);
#if FMT_USE_TYPEID
const std::type_info& ti = typeid(ex);
# ifdef FMT_HAS_ABI_CXA_DEMANGLE # ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0; int status = 0;
std::size_t size = 0; std::size_t size = 0;
@ -448,21 +473,85 @@ struct formatter<
} else { } else {
demangled_name_view = string_view(ti.name()); demangled_name_view = string_view(ti.name());
} }
out = detail::write_bytes(out, demangled_name_view, spec); return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION # elif FMT_MSC_VERSION
string_view demangled_name_view(ti.name()); const string_view demangled_name(ti.name());
if (demangled_name_view.starts_with("class ")) for (std::size_t i = 0; i < demangled_name.size(); ++i) {
demangled_name_view.remove_prefix(6); auto sub = demangled_name;
else if (demangled_name_view.starts_with("struct ")) sub.remove_prefix(i);
demangled_name_view.remove_prefix(7); if (sub.starts_with("enum ")) {
out = detail::write_bytes(out, demangled_name_view, spec); i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else # else
out = detail::write_bytes(out, string_view(ti.name()), spec); return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif # endif
}
} // namespace detail
FMT_EXPORT
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
}
};
#endif
FMT_EXPORT
template <typename T, typename Char>
struct formatter<
T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = FMT_USE_RTTI != 0;
}
return it;
}
template <typename Context>
auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
#if FMT_USE_RTTI
if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
*out++ = ':'; *out++ = ':';
*out++ = ' '; *out++ = ' ';
return detail::write_bytes(out, string_view(ex.what()), spec); }
#endif #endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
} }
}; };
@ -509,6 +598,14 @@ struct formatter<BitRef, Char,
} }
}; };
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
FMT_EXPORT FMT_EXPORT
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<std::atomic<T>, Char, struct formatter<std::atomic<T>, Char,
@ -533,5 +630,70 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
}; };
#endif // __cpp_lib_atomic_flag_test #endif // __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
template <typename FormatContext, typename OutputIt>
FMT_CONSTEXPR auto do_format(const std::complex<T>& c,
detail::dynamic_format_specs<Char>& specs,
FormatContext& ctx, OutputIt out) const
-> OutputIt {
if (c.real() != 0) {
*out++ = Char('(');
out = detail::write<Char>(out, c.real(), specs, ctx.locale());
specs.sign = sign::plus;
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
*out++ = Char(')');
return out;
}
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
return out;
}
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type_constant<T, Char>::value);
}
template <typename FormatContext>
auto format(const std::complex<T>& c, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
if (specs.width_ref.kind != detail::arg_id_kind::none ||
specs.precision_ref.kind != detail::arg_id_kind::none) {
detail::handle_dynamic_spec<detail::width_checker>(specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, ctx);
}
if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
auto buf = basic_memory_buffer<Char>();
auto outer_specs = format_specs();
outer_specs.width = specs.width;
outer_specs.fill = specs.fill;
outer_specs.align = specs.align;
specs.width = 0;
specs.fill = {};
specs.align = align::none;
do_format(c, specs, ctx, basic_appender<Char>(buf));
return detail::write<Char>(ctx.out(),
basic_string_view<Char>(buf.data(), buf.size()),
outer_specs);
}
};
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_STD_H_ #endif // FMT_STD_H_

View File

@ -8,12 +8,15 @@
#ifndef FMT_XCHAR_H_ #ifndef FMT_XCHAR_H_
#define FMT_XCHAR_H_ #define FMT_XCHAR_H_
#include <cwchar> #include "color.h"
#include "format.h" #include "format.h"
#include "ranges.h"
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_MODULE
# include <cwchar>
# if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale> # include <locale>
# endif
#endif #endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -22,9 +25,24 @@ namespace detail {
template <typename T> template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out, template <typename S, typename = void> struct format_string_char {};
loc_value value, const format_specs<wchar_t>& specs,
locale_ref loc) -> bool { template <typename S>
struct format_string_char<
S, void_t<decltype(sizeof(detail::to_string_view(std::declval<S>())))>> {
using type = char_t<S>;
};
template <typename S>
struct format_string_char<S, enable_if_t<is_compile_string<S>::value>> {
using type = typename S::char_type;
};
template <typename S>
using format_string_char_t = typename format_string_char<S>::type;
inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto& numpunct = auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>()); std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
@ -41,7 +59,7 @@ FMT_BEGIN_EXPORT
using wstring_view = basic_string_view<wchar_t>; using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>; using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>; using wformat_context = buffered_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>; using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>; using wmemory_buffer = basic_memory_buffer<wchar_t>;
@ -58,14 +76,18 @@ inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
#endif #endif
template <> struct is_char<wchar_t> : std::true_type {}; template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {}; template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {}; template <> struct is_char<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <>
struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled()> {};
#endif
template <typename... T> template <typename... T>
constexpr auto make_wformat_args(const T&... args) constexpr auto make_wformat_args(T&... args)
-> format_arg_store<wformat_context, T...> { -> decltype(fmt::make_format_args<wformat_context>(args...)) {
return {args...}; return fmt::make_format_args<wformat_context>(args...);
} }
inline namespace literals { inline namespace literals {
@ -96,9 +118,15 @@ auto join(std::initializer_list<T> list, wstring_view sep)
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }
template <typename... T>
auto join(const std::tuple<T...>& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str, auto vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, format_str, args); detail::vformat_to(buf, format_str, args);
@ -110,63 +138,74 @@ auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
} }
template <typename OutputIt, typename... T>
auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, 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... T, typename Char = char_t<S>, template <typename S, typename... T,
typename Char = detail::format_string_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)> !std::is_same<Char, wchar_t>::value)>
auto format(const S& format_str, T&&... args) -> std::basic_string<Char> { auto format(const S& format_str, T&&... 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<buffered_context<Char>>(args...));
} }
template <typename Locale, typename S, typename Char = char_t<S>, template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto vformat( inline auto vformat(const Locale& loc, const S& format_str,
const Locale& loc, const S& format_str, typename detail::vformat_args<Char>::type args)
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, detail::to_string_view(format_str), args); return detail::vformat(loc, detail::to_string_view(format_str), args);
} }
template <typename Locale, typename S, typename... T, typename Char = char_t<S>, template <typename Locale, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, T&&... args) inline auto format(const Locale& loc, const S& format_str, T&&... args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, detail::to_string_view(format_str), return detail::vformat(
fmt::make_format_args<buffer_context<Char>>(args...)); loc, detail::to_string_view(format_str),
fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename OutputIt, typename S, typename Char = char_t<S>, template <typename OutputIt, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& format_str, auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) typename detail::vformat_args<Char>::type args) -> OutputIt {
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(format_str), args); detail::vformat_to(buf, detail::to_string_view(format_str), args);
return detail::get_iterator(buf, out); return detail::get_iterator(buf, out);
} }
template <typename OutputIt, typename S, typename... T, template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_exotic_char<Char>::value)> !std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt { inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
return vformat_to(out, detail::to_string_view(fmt), return vformat_to(out, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename Locale, typename S, typename OutputIt, typename... Args, template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&& detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto vformat_to( inline auto vformat_to(OutputIt out, const Locale& loc, const S& format_str,
OutputIt out, const Locale& loc, const S& format_str, typename detail::vformat_args<Char>::type args)
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(format_str), args, vformat_to(buf, detail::to_string_view(format_str), args,
detail::locale_ref(loc)); detail::locale_ref(loc));
@ -174,7 +213,7 @@ inline auto vformat_to(
} }
template <typename OutputIt, typename Locale, typename S, typename... T, template <typename OutputIt, typename Locale, typename S, typename... T,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value && bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value && detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value> detail::is_exotic_char<Char>::value>
@ -182,15 +221,15 @@ inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
T&&... args) -> T&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(format_str), return vformat_to(out, loc, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename OutputIt, typename Char, typename... Args, template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n( inline auto vformat_to_n(OutputIt out, size_t n,
OutputIt out, size_t n, basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) typename detail::vformat_args<Char>::type args)
-> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits; using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n); auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
@ -199,21 +238,22 @@ inline auto vformat_to_n(
} }
template <typename OutputIt, typename S, typename... T, template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, detail::to_string_view(fmt), return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename S, typename... T, typename Char = char_t<S>, template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, T&&... args) -> size_t { inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<Char>(); auto buf = detail::counting_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), detail::vformat_to(buf, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
return buf.count(); return buf.count();
} }
@ -247,9 +287,32 @@ template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...)); return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
} }
/** inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
Converts *value* to ``std::wstring`` using the default format for type *T*. -> std::wstring {
*/ auto buf = wmemory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return fmt::to_string(buf);
}
template <typename... T>
inline auto format(const text_style& ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, const text_style& ts,
wformat_string<T...> fmt, const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(const text_style& ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
/// Converts `value` to `std::wstring` using the default format for type `T`.
template <typename T> inline auto to_wstring(const T& value) -> std::wstring { template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value); return format(FMT_STRING(L"{}"), value);
} }

View File

@ -13,29 +13,31 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp<float>; template FMT_API auto dragonbox::to_decimal(float x) noexcept
template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp<double>; -> dragonbox::decimal_fp<float>;
template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API locale_ref::locale_ref(const std::locale& loc); template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale; template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif #endif
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result<char>; template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<char>;
template FMT_API auto decimal_point_impl(locale_ref) -> char; template FMT_API auto decimal_point_impl(locale_ref) -> char;
template FMT_API void buffer<char>::append(const char*, const char*); template FMT_API void buffer<char>::append(const char*, const char*);
template FMT_API void vformat_to(buffer<char>&, template FMT_API void vformat_to(buffer<char>&, string_view,
string_view, typename vformat_args<>::type, locale_ref);
typename vformat_args<>::type,
locale_ref);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result<wchar_t>; template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<wchar_t>;
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*); template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);