Make format_specs not depend on code unit type

This commit is contained in:
Victor Zverovich
2024-01-17 07:15:50 -08:00
parent 090ee13595
commit 8510838db1
9 changed files with 115 additions and 122 deletions

View File

@@ -2037,6 +2037,7 @@ using unsigned_char = typename conditional_t<std::is_integral<Char>::value,
std::make_unsigned<Char>, std::make_unsigned<Char>,
type_identity<unsigned>>::type; type_identity<unsigned>>::type;
// Character (code unit) type is erased to prevent template bloat.
struct fill_t { struct fill_t {
private: private:
enum { max_size = 4 }; enum { max_size = 4 };
@@ -2106,7 +2107,7 @@ enum class presentation_type : unsigned char {
}; };
// Format specifiers for built-in and string types. // Format specifiers for built-in and string types.
template <typename Char = char> struct format_specs { struct format_specs {
int width; int width;
int precision; int precision;
presentation_type type; presentation_type type;
@@ -2160,8 +2161,7 @@ template <typename Char> struct arg_ref {
// Format specifiers with width and precision resolved at formatting rather // Format specifiers with width and precision resolved at formatting rather
// than parsing time to allow reusing the same parsed specifiers with // than parsing time to allow reusing the same parsed specifiers with
// different sets of arguments (precompilation of format strings). // different sets of arguments (precompilation of format strings).
template <typename Char = char> template <typename Char = char> struct dynamic_format_specs : format_specs {
struct dynamic_format_specs : format_specs<Char> {
arg_ref<Char> width_ref; arg_ref<Char> width_ref;
arg_ref<Char> precision_ref; arg_ref<Char> precision_ref;
}; };
@@ -2636,8 +2636,7 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
} }
// Checks char specs and returns true iff the presentation type is char-like. // Checks char specs and returns true iff the presentation type is char-like.
template <typename Char> FMT_CONSTEXPR inline auto check_char_specs(const format_specs& specs) -> bool {
FMT_CONSTEXPR auto check_char_specs(const format_specs<Char>& specs) -> bool {
if (specs.type != presentation_type::none && if (specs.type != presentation_type::none &&
specs.type != presentation_type::chr && specs.type != presentation_type::chr &&
specs.type != presentation_type::debug) { specs.type != presentation_type::debug) {

View File

@@ -1733,7 +1733,7 @@ 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 = specs.type =
precision >= 0 ? presentation_type::fixed : presentation_type::general; precision >= 0 ? presentation_type::fixed : presentation_type::general;
@@ -2076,7 +2076,7 @@ template <typename Char> struct formatter<weekday, Char> {
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;
@@ -2218,7 +2218,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

@@ -109,7 +109,7 @@ 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 #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
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
@@ -138,7 +138,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_});
} }

View File

@@ -999,7 +999,8 @@ constexpr auto compile_string_to_view(basic_string_view<Char> s)
} // namespace detail_exported } // namespace detail_exported
// A generic formatting context with custom output iterator and character // A generic formatting context with custom output iterator and character
// (code unit) support. // (code unit) support. Char is the format string code unit type which can be
// different from OutputIt::value_type.
template <typename OutputIt, typename Char> class generic_context { template <typename OutputIt, typename Char> class generic_context {
private: private:
OutputIt out_; OutputIt out_;
@@ -1068,7 +1069,7 @@ template <typename Locale> class format_facet : public Locale::facet {
protected: protected:
virtual auto do_put(appender out, loc_value val, virtual auto do_put(appender out, loc_value val,
const format_specs<>& specs) const -> bool; const format_specs& specs) const -> bool;
public: public:
static FMT_API typename Locale::id id; static FMT_API typename Locale::id id;
@@ -1081,7 +1082,7 @@ template <typename Locale> class format_facet : public Locale::facet {
grouping_(g.begin(), g.end()), grouping_(g.begin(), g.end()),
decimal_point_(decimal_point) {} decimal_point_(decimal_point) {}
auto put(appender out, loc_value val, const format_specs<>& specs) const auto put(appender out, loc_value val, const format_specs& specs) const
-> bool { -> bool {
return do_put(out, val, specs); return do_put(out, val, specs);
} }
@@ -1727,7 +1728,7 @@ FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, const fill_t& fill)
// width: output display width in (terminal) column positions. // width: output display width in (terminal) column positions.
template <typename Char, align::type align = align::left, typename OutputIt, template <typename Char, align::type align = align::left, typename OutputIt,
typename F> typename F>
FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs<Char>& specs, FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs& specs,
size_t size, size_t width, F&& f) -> OutputIt { size_t size, size_t width, F&& f) -> OutputIt {
static_assert(align == align::left || align == align::right, ""); static_assert(align == align::left || align == align::right, "");
unsigned spec_width = to_unsigned(specs.width); unsigned spec_width = to_unsigned(specs.width);
@@ -1746,14 +1747,14 @@ FMT_CONSTEXPR auto write_padded(OutputIt out, const format_specs<Char>& specs,
template <typename Char, align::type align = align::left, typename OutputIt, template <typename Char, align::type align = align::left, typename OutputIt,
typename F> typename F>
constexpr auto write_padded(OutputIt out, const format_specs<Char>& specs, constexpr auto write_padded(OutputIt out, const format_specs& specs,
size_t size, F&& f) -> OutputIt { size_t size, F&& f) -> OutputIt {
return write_padded<Char, align>(out, specs, size, size, f); return write_padded<Char, align>(out, specs, size, size, f);
} }
template <typename Char, align::type align = align::left, typename OutputIt> template <typename Char, align::type align = align::left, typename OutputIt>
FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes, FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes,
const format_specs<Char>& specs) -> OutputIt { const format_specs& specs = {}) -> OutputIt {
return write_padded<Char, align>( return write_padded<Char, align>(
out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) { out, specs, bytes.size(), [bytes](reserve_iterator<OutputIt> it) {
const char* data = bytes.data(); const char* data = bytes.data();
@@ -1762,7 +1763,7 @@ FMT_CONSTEXPR auto write_bytes(OutputIt out, string_view bytes,
} }
template <typename Char, typename OutputIt, typename UIntPtr> template <typename Char, typename OutputIt, typename UIntPtr>
auto write_ptr(OutputIt out, UIntPtr value, const format_specs<Char>* specs) auto write_ptr(OutputIt out, UIntPtr value, const format_specs* specs)
-> OutputIt { -> OutputIt {
int num_digits = count_digits<4>(value); int num_digits = count_digits<4>(value);
auto size = to_unsigned(num_digits) + size_t(2); auto size = to_unsigned(num_digits) + size_t(2);
@@ -1926,7 +1927,7 @@ auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write_char(OutputIt out, Char value, FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
const format_specs<Char>& specs) -> OutputIt { const format_specs& specs) -> OutputIt {
bool is_debug = specs.type == presentation_type::debug; bool is_debug = specs.type == presentation_type::debug;
return write_padded<Char>(out, specs, 1, [=](reserve_iterator<OutputIt> it) { return write_padded<Char>(out, specs, 1, [=](reserve_iterator<OutputIt> it) {
if (is_debug) return write_escaped_char(it, value); if (is_debug) return write_escaped_char(it, value);
@@ -1935,15 +1936,14 @@ FMT_CONSTEXPR auto write_char(OutputIt out, Char value,
}); });
} }
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write(OutputIt out, Char value, FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs,
const format_specs<Char>& specs, locale_ref loc = {}) locale_ref loc = {}) -> OutputIt {
-> OutputIt {
// char is formatted as unsigned char for consistency across platforms. // char is formatted as unsigned char for consistency across platforms.
using unsigned_type = using unsigned_type =
conditional_t<std::is_same<Char, char>::value, unsigned char, unsigned>; conditional_t<std::is_same<Char, char>::value, unsigned char, unsigned>;
return check_char_specs(specs) return check_char_specs(specs)
? write_char<Char>(out, value, specs) ? write_char<Char>(out, value, specs)
: write(out, static_cast<unsigned_type>(value), specs, loc); : write<Char>(out, static_cast<unsigned_type>(value), specs, loc);
} }
// Data for write_int that doesn't depend on output iterator type. It is used to // Data for write_int that doesn't depend on output iterator type. It is used to
@@ -1953,7 +1953,7 @@ template <typename Char> struct write_int_data {
size_t padding; size_t padding;
FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix, FMT_CONSTEXPR write_int_data(int num_digits, unsigned prefix,
const format_specs<Char>& specs) const format_specs& specs)
: size((prefix >> 24) + to_unsigned(num_digits)), padding(0) { : size((prefix >> 24) + to_unsigned(num_digits)), padding(0) {
if (specs.align == align::numeric) { if (specs.align == align::numeric) {
auto width = to_unsigned(specs.width); auto width = to_unsigned(specs.width);
@@ -1975,7 +1975,7 @@ template <typename Char> struct write_int_data {
template <typename Char, typename OutputIt, typename W> template <typename Char, typename OutputIt, typename W>
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits,
unsigned prefix, unsigned prefix,
const format_specs<Char>& specs, const format_specs& specs,
W write_digits) -> OutputIt { W write_digits) -> OutputIt {
// Slightly faster check for specs.width == 0 && specs.precision == -1. // Slightly faster check for specs.width == 0 && specs.precision == -1.
if ((specs.width | (specs.precision + 1)) == 0) { if ((specs.width | (specs.precision + 1)) == 0) {
@@ -2068,8 +2068,8 @@ FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
// Writes a decimal integer with digit grouping. // Writes a decimal integer with digit grouping.
template <typename OutputIt, typename UInt, typename Char> template <typename OutputIt, typename UInt, typename Char>
auto write_int(OutputIt out, UInt value, unsigned prefix, auto write_int(OutputIt out, UInt value, unsigned prefix,
const format_specs<Char>& specs, const format_specs& specs, const digit_grouping<Char>& grouping)
const digit_grouping<Char>& grouping) -> OutputIt { -> OutputIt {
static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, ""); static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
int num_digits = 0; int num_digits = 0;
auto buffer = memory_buffer(); auto buffer = memory_buffer();
@@ -2117,11 +2117,11 @@ auto write_int(OutputIt out, UInt value, unsigned prefix,
} }
// Writes a localized value. // Writes a localized value.
FMT_API auto write_loc(appender out, loc_value value, FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs,
const format_specs<>& specs, locale_ref loc) -> bool; locale_ref loc) -> bool;
template <typename OutputIt, typename Char> template <typename OutputIt>
inline auto write_loc(OutputIt, loc_value, const format_specs<Char>&, inline auto write_loc(OutputIt, loc_value, const format_specs&, locale_ref)
locale_ref) -> bool { -> bool {
return false; return false;
} }
@@ -2148,7 +2148,7 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign)
template <typename Char = char> struct loc_writer { template <typename Char = char> struct loc_writer {
basic_appender<Char> out; basic_appender<Char> out;
const format_specs<Char>& specs; const format_specs& specs;
std::basic_string<Char> sep; std::basic_string<Char> sep;
std::string grouping; std::string grouping;
std::basic_string<Char> decimal_point; std::basic_string<Char> decimal_point;
@@ -2169,8 +2169,8 @@ template <typename Char = char> struct loc_writer {
template <typename Char, typename OutputIt, typename T> template <typename Char, typename OutputIt, typename T>
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg, FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
const format_specs<Char>& specs, const format_specs& specs, locale_ref)
locale_ref) -> OutputIt { -> OutputIt {
static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, ""); static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
auto abs_value = arg.abs_value; auto abs_value = arg.abs_value;
auto prefix = arg.prefix; auto prefix = arg.prefix;
@@ -2221,18 +2221,19 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
return out; return out;
} }
template <typename Char, typename OutputIt, typename T> template <typename Char, typename OutputIt, typename T>
FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline(OutputIt out,
OutputIt out, write_int_arg<T> arg, const format_specs<Char>& specs, write_int_arg<T> arg,
const format_specs& specs,
locale_ref loc) -> OutputIt { locale_ref loc) -> OutputIt {
return write_int<Char>(out, arg, specs, loc); return write_int<Char>(out, arg, specs, loc);
} }
template <typename Char, typename OutputIt, typename T, template <typename Char, typename T,
FMT_ENABLE_IF(is_integral<T>::value && FMT_ENABLE_IF(is_integral<T>::value &&
!std::is_same<T, bool>::value && !std::is_same<T, bool>::value &&
std::is_same<OutputIt, basic_appender<Char>>::value)> !std::is_same<T, Char>::value)>
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, FMT_CONSTEXPR FMT_INLINE auto write(basic_appender<Char> out, T value,
const format_specs<Char>& specs, const format_specs& specs, locale_ref loc)
locale_ref loc) -> OutputIt { -> basic_appender<Char> {
if (specs.localized && write_loc(out, value, specs, loc)) return out; if (specs.localized && write_loc(out, value, specs, loc)) return out;
return write_int_noinline<Char>(out, make_write_int_arg(value, specs.sign), return write_int_noinline<Char>(out, make_write_int_arg(value, specs.sign),
specs, loc); specs, loc);
@@ -2241,10 +2242,11 @@ FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_integral<T>::value && FMT_ENABLE_IF(is_integral<T>::value &&
!std::is_same<T, bool>::value && !std::is_same<T, bool>::value &&
!std::is_same<T, Char>::value &&
!std::is_same<OutputIt, basic_appender<Char>>::value)> !std::is_same<OutputIt, basic_appender<Char>>::value)>
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
const format_specs<Char>& specs, const format_specs& specs, locale_ref loc)
locale_ref loc) -> OutputIt { -> OutputIt {
if (specs.localized && write_loc(out, value, specs, loc)) return out; if (specs.localized && write_loc(out, value, specs, loc)) return out;
return write_int<Char>(out, make_write_int_arg(value, specs.sign), specs, return write_int<Char>(out, make_write_int_arg(value, specs.sign), specs,
loc); loc);
@@ -2292,7 +2294,7 @@ class counting_iterator {
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s, FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
const format_specs<Char>& specs) -> OutputIt { const format_specs& specs) -> OutputIt {
auto data = s.data(); auto data = s.data();
auto size = s.size(); auto size = s.size();
if (specs.precision >= 0 && to_unsigned(specs.precision) < size) if (specs.precision >= 0 && to_unsigned(specs.precision) < size)
@@ -2317,14 +2319,12 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view<Char> s,
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write(OutputIt out, FMT_CONSTEXPR auto write(OutputIt out,
basic_string_view<type_identity_t<Char>> s, basic_string_view<type_identity_t<Char>> s,
const format_specs<Char>& specs, locale_ref) const format_specs& specs, locale_ref) -> OutputIt {
-> OutputIt {
return write<Char>(out, s, specs); return write<Char>(out, s, specs);
} }
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write(OutputIt out, const Char* s, FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs,
const format_specs<Char>& specs, locale_ref) locale_ref) -> OutputIt {
-> OutputIt {
if (specs.type == presentation_type::pointer) if (specs.type == presentation_type::pointer)
return write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs); return write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
if (!s) report_error("string pointer is null"); if (!s) report_error("string pointer is null");
@@ -2356,7 +2356,7 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
// DEPRECATED! // DEPRECATED!
template <typename Char> template <typename Char>
FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
format_specs<Char>& specs) -> const Char* { format_specs& specs) -> const Char* {
FMT_ASSERT(begin != end, ""); FMT_ASSERT(begin != end, "");
auto align = align::none; auto align = align::none;
auto p = begin + code_point_length(begin); auto p = begin + code_point_length(begin);
@@ -2414,15 +2414,14 @@ struct float_specs {
bool showpoint : 1; bool showpoint : 1;
}; };
template <typename Char> // DEPRECATED!
FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs) FMT_CONSTEXPR inline auto parse_float_type_spec(const format_specs& specs)
-> float_specs { -> float_specs {
auto result = float_specs(); auto result = float_specs();
result.showpoint = specs.alt; result.showpoint = specs.alt;
result.locale = specs.localized; result.locale = specs.localized;
switch (specs.type) { switch (specs.type) {
default: default:
FMT_ASSERT(false, "");
FMT_FALLTHROUGH; FMT_FALLTHROUGH;
case presentation_type::none: case presentation_type::none:
result.format = float_format::general; result.format = float_format::general;
@@ -2451,7 +2450,7 @@ FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs)
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan,
format_specs<Char> specs, format_specs specs,
const float_specs& fspecs) -> OutputIt { const float_specs& fspecs) -> OutputIt {
auto str = auto str =
isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf"); isnan ? (fspecs.upper ? "NAN" : "nan") : (fspecs.upper ? "INF" : "inf");
@@ -2573,10 +2572,10 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
buffer.end(), out); buffer.end(), out);
} }
template <typename OutputIt, typename DecimalFP, typename Char, template <typename Char, typename OutputIt, typename DecimalFP,
typename Grouping = digit_grouping<Char>> typename Grouping = digit_grouping<Char>>
FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
const format_specs<Char>& specs, const format_specs& specs,
float_specs fspecs, locale_ref loc) float_specs fspecs, locale_ref loc)
-> OutputIt { -> OutputIt {
auto significand = f.significand; auto significand = f.significand;
@@ -2694,17 +2693,16 @@ template <typename Char> class fallback_digit_grouping {
} }
}; };
template <typename OutputIt, typename DecimalFP, typename Char> template <typename Char, typename OutputIt, typename DecimalFP>
FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
const format_specs<Char>& specs, const format_specs& specs, float_specs fspecs,
float_specs fspecs, locale_ref loc) locale_ref loc) -> OutputIt {
-> OutputIt {
if (is_constant_evaluated()) { if (is_constant_evaluated()) {
return do_write_float<OutputIt, DecimalFP, Char, return do_write_float<Char, OutputIt, DecimalFP,
fallback_digit_grouping<Char>>(out, f, specs, fspecs, fallback_digit_grouping<Char>>(out, f, specs, fspecs,
loc); loc);
} else { } else {
return do_write_float(out, f, specs, fspecs, loc); return do_write_float<Char>(out, f, specs, fspecs, loc);
} }
} }
@@ -3561,9 +3559,8 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
return exp; return exp;
} }
template <typename Char, typename OutputIt, typename T> template <typename Char, typename OutputIt, typename T>
FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
format_specs<Char> specs, locale_ref loc) locale_ref loc) -> OutputIt {
-> OutputIt {
float_specs fspecs = parse_float_type_spec(specs); float_specs fspecs = parse_float_type_spec(specs);
fspecs.sign = specs.sign; fspecs.sign = specs.sign;
if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit. if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit.
@@ -3574,7 +3571,7 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value,
} }
if (!detail::isfinite(value)) if (!detail::isfinite(value))
return write_nonfinite(out, detail::isnan(value), specs, fspecs); return write_nonfinite<Char>(out, detail::isnan(value), specs, fspecs);
if (specs.align == align::numeric && fspecs.sign) { if (specs.align == align::numeric && fspecs.sign) {
auto it = reserve(out, 1); auto it = reserve(out, 1);
@@ -3606,23 +3603,23 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value,
int exp = format_float(convert_float(value), precision, fspecs, buffer); int exp = format_float(convert_float(value), precision, fspecs, buffer);
fspecs.precision = precision; fspecs.precision = precision;
auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp}; auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
return write_float(out, f, specs, fspecs, loc); return write_float<Char>(out, f, specs, fspecs, loc);
} }
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_floating_point<T>::value)> FMT_ENABLE_IF(is_floating_point<T>::value)>
FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs<Char> specs, FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs,
locale_ref loc = {}) -> OutputIt { locale_ref loc = {}) -> OutputIt {
if (const_check(!is_supported_floating_point(value))) return out; if (const_check(!is_supported_floating_point(value))) return out;
return specs.localized && write_loc(out, value, specs, loc) return specs.localized && write_loc(out, value, specs, loc)
? out ? out
: write_float(out, value, specs, loc); : write_float<Char>(out, value, specs, loc);
} }
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_fast_float<T>::value)> FMT_ENABLE_IF(is_fast_float<T>::value)>
FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
if (is_constant_evaluated()) return write(out, value, format_specs<Char>()); if (is_constant_evaluated()) return write<Char>(out, value, format_specs());
if (const_check(!is_supported_floating_point(value))) return out; if (const_check(!is_supported_floating_point(value))) return out;
auto fspecs = float_specs(); auto fspecs = float_specs();
@@ -3631,26 +3628,26 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
value = -value; value = -value;
} }
constexpr auto specs = format_specs<Char>(); constexpr auto specs = format_specs();
using floaty = conditional_t<std::is_same<T, long double>::value, double, T>; using floaty = conditional_t<std::is_same<T, long double>::value, double, T>;
using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint; using floaty_uint = typename dragonbox::float_info<floaty>::carrier_uint;
floaty_uint mask = exponent_mask<floaty>(); floaty_uint mask = exponent_mask<floaty>();
if ((bit_cast<floaty_uint>(value) & mask) == mask) if ((bit_cast<floaty_uint>(value) & mask) == mask)
return write_nonfinite(out, std::isnan(value), specs, fspecs); return write_nonfinite<Char>(out, std::isnan(value), specs, fspecs);
auto dec = dragonbox::to_decimal(static_cast<floaty>(value)); auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
return write_float(out, dec, specs, fspecs, {}); return write_float<Char>(out, dec, specs, fspecs, {});
} }
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_floating_point<T>::value && FMT_ENABLE_IF(is_floating_point<T>::value &&
!is_fast_float<T>::value)> !is_fast_float<T>::value)>
inline auto write(OutputIt out, T value) -> OutputIt { inline auto write(OutputIt out, T value) -> OutputIt {
return write(out, value, format_specs<Char>()); return write<Char>(out, value, format_specs());
} }
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
auto write(OutputIt out, monostate, format_specs<Char> = {}, locale_ref = {}) auto write(OutputIt out, monostate, format_specs = {}, locale_ref = {})
-> OutputIt { -> OutputIt {
FMT_ASSERT(false, ""); FMT_ASSERT(false, "");
return out; return out;
@@ -3684,9 +3681,8 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt {
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(std::is_same<T, bool>::value)> FMT_ENABLE_IF(std::is_same<T, bool>::value)>
FMT_CONSTEXPR auto write(OutputIt out, T value, FMT_CONSTEXPR auto write(OutputIt out, T value, const format_specs& specs = {},
const format_specs<Char>& specs = {}, locale_ref = {}) locale_ref = {}) -> OutputIt {
-> OutputIt {
return specs.type != presentation_type::none && return specs.type != presentation_type::none &&
specs.type != presentation_type::string specs.type != presentation_type::string
? write<Char>(out, value ? 1 : 0, specs, {}) ? write<Char>(out, value ? 1 : 0, specs, {})
@@ -3709,7 +3705,7 @@ FMT_CONSTEXPR20 auto write(OutputIt out, const Char* value) -> OutputIt {
template <typename Char, typename OutputIt, typename T, template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(std::is_same<T, void>::value)> FMT_ENABLE_IF(std::is_same<T, void>::value)>
auto write(OutputIt out, const T* value, const format_specs<Char>& specs = {}, auto write(OutputIt out, const T* value, const format_specs& specs = {},
locale_ref = {}) -> OutputIt { locale_ref = {}) -> OutputIt {
return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs); return write_ptr<Char>(out, bit_cast<uintptr_t>(value), &specs);
} }
@@ -3764,7 +3760,7 @@ template <typename Char> struct arg_formatter {
using context = buffered_context<Char>; using context = buffered_context<Char>;
iterator out; iterator out;
const format_specs<Char>& specs; const format_specs& specs;
locale_ref locale; locale_ref locale;
template <typename T> template <typename T>
@@ -4094,7 +4090,7 @@ template <> struct formatter<bytes> {
specs.width_ref, ctx); specs.width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>( detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, ctx); specs.precision, specs.precision_ref, ctx);
return detail::write_bytes(ctx.out(), b.data_, specs); return detail::write_bytes<char>(ctx.out(), b.data_, specs);
} }
}; };
@@ -4184,7 +4180,7 @@ template <typename T> struct nested_formatter {
if (width_ == 0) return write(ctx.out()); if (width_ == 0) return write(ctx.out());
auto buf = memory_buffer(); auto buf = memory_buffer();
write(appender(buf)); write(appender(buf));
auto specs = format_specs<>(); auto specs = format_specs();
specs.width = width_; specs.width = width_;
specs.fill = fill_; specs.fill = fill_;
specs.align = align_; specs.align = align_;

View File

@@ -191,12 +191,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 {
@@ -220,7 +220,7 @@ 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(basic_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()};
} }
@@ -237,11 +237,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(basic_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) {}
@@ -255,19 +255,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 = ' '; // 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)>
@@ -309,7 +308,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 '-':
@@ -334,7 +333,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;
@@ -365,7 +364,7 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
specs.width = static_cast<int>( specs.width = static_cast<int>(
get_arg(-1).visit(detail::printf_width_handler<Char>(specs))); get_arg(-1).visit(detail::printf_width_handler(specs)));
} }
} }
return arg_index; return arg_index;
@@ -450,7 +449,7 @@ 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.

View File

@@ -117,7 +117,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;
@@ -372,7 +372,7 @@ 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;
@@ -403,10 +403,9 @@ struct formatter<
template <typename Context> template <typename Context>
auto format(const std::exception& ex, Context& ctx) const auto format(const std::exception& ex, Context& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
format_specs<Char> spec;
auto out = ctx.out(); auto out = ctx.out();
if (!with_typename_) if (!with_typename_)
return detail::write_bytes(out, string_view(ex.what()), spec); return detail::write_bytes<Char>(out, string_view(ex.what()));
#if FMT_USE_TYPEID #if FMT_USE_TYPEID
const std::type_info& ti = typeid(ex); const std::type_info& ti = typeid(ex);
@@ -448,20 +447,21 @@ 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); out = detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION # elif FMT_MSC_VERSION
string_view demangled_name_view(ti.name()); string_view demangled_name_view(ti.name());
if (demangled_name_view.starts_with("class ")) if (demangled_name_view.starts_with("class "))
demangled_name_view.remove_prefix(6); demangled_name_view.remove_prefix(6);
else if (demangled_name_view.starts_with("struct ")) else if (demangled_name_view.starts_with("struct "))
demangled_name_view.remove_prefix(7); demangled_name_view.remove_prefix(7);
out = detail::write_bytes(out, demangled_name_view, spec); out = detail::write_bytes<Char>(out, demangled_name_view);
# else # else
out = detail::write_bytes(out, string_view(ti.name()), spec); out = detail::write_bytes<Char>(out, string_view(ti.name())
});
# endif # endif
*out++ = ':'; *out++ = ':';
*out++ = ' '; *out++ = ' ';
return detail::write_bytes(out, string_view(ex.what()), spec); return detail::write_bytes<Char>(out, string_view(ex.what()));
#endif #endif
} }
}; };

View File

@@ -47,8 +47,7 @@ template <typename S>
using format_string_char_t = typename format_string_char<S>::type; using format_string_char_t = typename format_string_char<S>::type;
inline auto write_loc(basic_appender<wchar_t> out, loc_value value, inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
const format_specs<wchar_t>& specs, locale_ref loc) const format_specs& specs, locale_ref loc) -> bool {
-> 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>());

View File

@@ -2188,11 +2188,11 @@ class format_facet : public fmt::format_facet<std::locale> {
}; };
auto do_put(fmt::appender out, fmt::loc_value val, auto do_put(fmt::appender out, fmt::loc_value val,
const fmt::format_specs<>&) const -> bool override; const fmt::format_specs&) const -> bool override;
}; };
auto format_facet::do_put(fmt::appender out, fmt::loc_value val, auto format_facet::do_put(fmt::appender out, fmt::loc_value val,
const fmt::format_specs<>&) const -> bool { const fmt::format_specs&) const -> bool {
return val.visit(int_formatter{out}); return val.visit(int_formatter{out});
} }

View File

@@ -434,7 +434,7 @@ class scan_context {
namespace detail { namespace detail {
const char* parse_scan_specs(const char* begin, const char* end, const char* parse_scan_specs(const char* begin, const char* end,
format_specs<>& specs, scan_type) { format_specs& specs, scan_type) {
while (begin != end) { while (begin != end) {
switch (to_ascii(*begin)) { switch (to_ascii(*begin)) {
// TODO: parse more scan format specifiers // TODO: parse more scan format specifiers
@@ -506,14 +506,14 @@ auto read_hex(scan_iterator it, T& value) -> scan_iterator {
} }
template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)>
auto read(scan_iterator it, T& value, const format_specs<>& specs) auto read(scan_iterator it, T& value, const format_specs& specs)
-> scan_iterator { -> scan_iterator {
if (specs.type == presentation_type::hex) return read_hex(it, value); if (specs.type == presentation_type::hex) return read_hex(it, value);
return read(it, value); return read(it, value);
} }
template <typename T, FMT_ENABLE_IF(std::is_signed<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_signed<T>::value)>
auto read(scan_iterator it, T& value, const format_specs<>& specs = {}) auto read(scan_iterator it, T& value, const format_specs& specs = {})
-> scan_iterator { -> scan_iterator {
bool negative = it != scan_sentinel() && *it == '-'; bool negative = it != scan_sentinel() && *it == '-';
if (negative) { if (negative) {
@@ -528,13 +528,13 @@ auto read(scan_iterator it, T& value, const format_specs<>& specs = {})
return it; return it;
} }
auto read(scan_iterator it, std::string& value, const format_specs<>& = {}) auto read(scan_iterator it, std::string& value, const format_specs& = {})
-> scan_iterator { -> scan_iterator {
while (it != scan_sentinel() && *it != ' ') value.push_back(*it++); while (it != scan_sentinel() && *it != ' ') value.push_back(*it++);
return it; return it;
} }
auto read(scan_iterator it, string_view& value, const format_specs<>& = {}) auto read(scan_iterator it, string_view& value, const format_specs& = {})
-> scan_iterator { -> scan_iterator {
auto range = to_contiguous(it); auto range = to_contiguous(it);
// This could also be checked at compile time in scan. // This could also be checked at compile time in scan.
@@ -546,7 +546,7 @@ auto read(scan_iterator it, string_view& value, const format_specs<>& = {})
return advance(it, size); return advance(it, size);
} }
auto read(scan_iterator it, monostate, const format_specs<>& = {}) auto read(scan_iterator it, monostate, const format_specs& = {})
-> scan_iterator { -> scan_iterator {
return it; return it;
} }
@@ -563,7 +563,7 @@ struct default_arg_scanner {
// An argument scanner with format specifiers. // An argument scanner with format specifiers.
struct arg_scanner { struct arg_scanner {
scan_iterator it; scan_iterator it;
const format_specs<>& specs; const format_specs& specs;
template <typename T> auto operator()(T&& value) -> scan_iterator { template <typename T> auto operator()(T&& value) -> scan_iterator {
return read(it, value, specs); return read(it, value, specs);
@@ -617,7 +617,7 @@ struct scan_handler {
scan_arg arg = scan_ctx_.arg(arg_id); scan_arg arg = scan_ctx_.arg(arg_id);
if (arg.scan_custom(begin, parse_ctx_, scan_ctx_)) if (arg.scan_custom(begin, parse_ctx_, scan_ctx_))
return parse_ctx_.begin(); return parse_ctx_.begin();
auto specs = format_specs<>(); auto specs = format_specs();
begin = parse_scan_specs(begin, end, specs, arg.type()); begin = parse_scan_specs(begin, end, specs, arg.type());
if (begin == end || *begin != '}') on_error("missing '}' in format string"); if (begin == end || *begin != '}') on_error("missing '}' in format string");
scan_ctx_.advance_to(arg.visit(arg_scanner{scan_ctx_.begin(), specs})); scan_ctx_.advance_to(arg.visit(arg_scanner{scan_ctx_.begin(), specs}));