Refactor format_specs for #1109 and #940

Refactor `format_specs` and related APIs to support variable-width fill
(#1109), improve naming consistency, remove legacy setters (#940), and
optimize layout.
This commit is contained in:
Victor Zverovich 2019-07-06 17:57:57 -07:00
parent 8e0dcd20b3
commit e4f84ee1c6
8 changed files with 195 additions and 176 deletions

View File

@ -715,7 +715,7 @@ struct chrono_formatter {
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:
align_spec spec; basic_format_specs<Char> spec;
int precision; int precision;
typedef internal::arg_ref<Char> arg_ref_type; typedef internal::arg_ref<Char> arg_ref_type;
arg_ref_type width_ref; arg_ref_type width_ref;
@ -744,9 +744,9 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
} }
void on_error(const char* msg) { FMT_THROW(format_error(msg)); } void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_fill(Char fill) { f.spec.fill_ = fill; } void on_fill(Char fill) { f.spec.fill[0] = fill; }
void on_align(alignment align) { f.spec.align_ = align; } void on_align(align_t align) { f.spec.align = align; }
void on_width(unsigned width) { f.spec.width_ = width; } void on_width(unsigned width) { f.spec.width = width; }
void on_precision(unsigned precision) { f.precision = precision; } void on_precision(unsigned precision) { f.precision = precision; }
void end_precision() {} void end_precision() {}
@ -804,7 +804,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
using range = internal::output_range<decltype(ctx.out()), Char>; using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out())); internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>( internal::handle_dynamic_spec<internal::width_checker>(
spec.width_, width_ref, ctx, format_str.begin()); spec.width, width_ref, ctx, format_str.begin());
internal::handle_dynamic_spec<internal::precision_checker>( internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx, format_str.begin()); precision, precision_ref, ctx, format_str.begin());
if (begin == end || *begin == '}') { if (begin == end || *begin == '}') {

View File

@ -763,7 +763,7 @@ char* sprintf_format(Double value, internal::buffer<char>& buf,
char format[max_format_size]; char format[max_format_size];
char* format_ptr = format; char* format_ptr = format;
*format_ptr++ = '%'; *format_ptr++ = '%';
if (spec.has(HASH_FLAG) || !spec.type) *format_ptr++ = '#'; if (spec.alt || !spec.type) *format_ptr++ = '#';
if (spec.precision >= 0) { if (spec.precision >= 0) {
*format_ptr++ = '.'; *format_ptr++ = '.';
*format_ptr++ = '*'; *format_ptr++ = '*';

View File

@ -898,53 +898,74 @@ FMT_API void format_windows_error(fmt::internal::buffer<char>& out,
#endif #endif
template <typename T = void> struct null {}; template <typename T = void> struct null {};
// Workaround an array initialization issue in gcc 4.8.
template <typename Char> struct fill_t {
private:
Char data_[6];
public:
FMT_CONSTEXPR Char& operator[](size_t index) { return data_[index]; }
FMT_CONSTEXPR const Char& operator[](size_t index) const {
return data_[index];
}
static FMT_CONSTEXPR fill_t<Char> make() {
auto fill = fill_t<Char>();
fill[0] = Char(' ');
return fill;
}
};
} // namespace internal } // namespace internal
enum alignment { // We cannot use enum classes as bit fields because of a gcc bug
ALIGN_DEFAULT, // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61414.
ALIGN_LEFT, namespace align {
ALIGN_RIGHT, enum type { none, left, right, center, numeric };
ALIGN_CENTER, }
ALIGN_NUMERIC using align_t = align::type;
namespace sign {
enum type { none, minus, plus, space };
}
using sign_t = sign::type;
// Format specifiers for built-in and string types.
template <typename Char> struct basic_format_specs {
int width;
int precision;
char type;
align_t align : 4;
sign_t sign : 3;
bool alt : 1; // Alternate form ('#').
internal::fill_t<Char> fill;
constexpr basic_format_specs()
: width(0),
precision(-1),
type(0),
align(align::none),
sign(sign::none),
alt(false),
fill(internal::fill_t<Char>::make()) {}
}; };
// Flags. using format_specs = basic_format_specs<char>;
enum { SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8 };
// An alignment specifier. namespace internal {
struct align_spec {
unsigned width_;
// Fill is always wchar_t and cast to char if necessary to avoid having
// two specialization of align_spec and its subclasses.
wchar_t fill_;
alignment align_;
FMT_CONSTEXPR align_spec() : width_(0), fill_(' '), align_(ALIGN_DEFAULT) {}
FMT_CONSTEXPR unsigned width() const { return width_; }
FMT_CONSTEXPR wchar_t fill() const { return fill_; }
FMT_CONSTEXPR alignment align() const { return align_; }
};
struct core_format_specs { struct core_format_specs {
int precision; int precision;
uint_least8_t flags;
char type; char type;
bool alt : 1;
FMT_CONSTEXPR core_format_specs() : precision(-1), flags(0), type(0) {}
FMT_CONSTEXPR bool has(unsigned f) const { return (flags & f) != 0; }
FMT_CONSTEXPR bool has_precision() const { return precision != -1; }
};
// Format specifiers.
template <typename Char> template <typename Char>
struct basic_format_specs : align_spec, core_format_specs { constexpr core_format_specs(basic_format_specs<Char> specs)
FMT_CONSTEXPR basic_format_specs() {} : precision(specs.precision), type(specs.type), alt(specs.alt) {}
constexpr bool has_precision() const { return precision >= 0; }
}; };
typedef basic_format_specs<char> format_specs;
namespace internal {
namespace grisu_options { namespace grisu_options {
enum { fixed = 1, grisu3 = 2 }; enum { fixed = 1, grisu3 = 2 };
} }
@ -1117,7 +1138,7 @@ FMT_CONSTEXPR void handle_char_specs(const basic_format_specs<Char>* specs,
Handler&& handler) { Handler&& handler) {
if (!specs) return handler.on_char(); if (!specs) return handler.on_char();
if (specs->type && specs->type != 'c') return handler.on_int(); if (specs->type && specs->type != 'c') return handler.on_int();
if (specs->align() == ALIGN_NUMERIC || specs->flags != 0) if (specs->align == align::numeric || specs->sign != sign::none || specs->alt)
handler.on_error("invalid format specifier for char"); handler.on_error("invalid format specifier for char");
handler.on_char(); handler.on_char();
} }
@ -1262,20 +1283,20 @@ template <typename Range> class basic_writer {
template <typename Spec, typename F> template <typename Spec, typename F>
void write_int(int num_digits, string_view prefix, const Spec& spec, F f) { void write_int(int num_digits, string_view prefix, const Spec& spec, F f) {
std::size_t size = prefix.size() + internal::to_unsigned(num_digits); std::size_t size = prefix.size() + internal::to_unsigned(num_digits);
char_type fill = static_cast<char_type>(spec.fill()); char_type fill = spec.fill[0];
std::size_t padding = 0; std::size_t padding = 0;
if (spec.align() == ALIGN_NUMERIC) { if (spec.align == align::numeric) {
if (spec.width() > size) { if (internal::to_unsigned(spec.width) > size) {
padding = spec.width() - size; padding = spec.width - size;
size = spec.width(); size = spec.width;
} }
} else if (spec.precision > num_digits) { } else if (spec.precision > num_digits) {
size = prefix.size() + internal::to_unsigned(spec.precision); size = prefix.size() + internal::to_unsigned(spec.precision);
padding = internal::to_unsigned(spec.precision - num_digits); padding = internal::to_unsigned(spec.precision - num_digits);
fill = static_cast<char_type>('0'); fill = static_cast<char_type>('0');
} }
align_spec as = spec; format_specs as = spec;
if (spec.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT; if (spec.align == align::none) as.align = align::right;
write_padded(as, padded_int_writer<F>{size, prefix, fill, padding, f}); write_padded(as, padded_int_writer<F>{size, prefix, fill, padding, f});
} }
@ -1312,8 +1333,8 @@ template <typename Range> class basic_writer {
prefix[0] = '-'; prefix[0] = '-';
++prefix_size; ++prefix_size;
abs_value = 0 - abs_value; abs_value = 0 - abs_value;
} else if (spec.has(SIGN_FLAG)) { } else if (spec.sign != sign::none && spec.sign != sign::minus) {
prefix[0] = spec.has(PLUS_FLAG) ? '+' : ' '; prefix[0] = spec.sign == sign::plus ? '+' : ' ';
++prefix_size; ++prefix_size;
} }
} }
@ -1344,9 +1365,9 @@ template <typename Range> class basic_writer {
}; };
void on_hex() { void on_hex() {
if (spec.has(HASH_FLAG)) { if (spec.alt) {
prefix[prefix_size++] = '0'; prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(spec.type); prefix[prefix_size++] = spec.type;
} }
int num_digits = internal::count_digits<4>(abs_value); int num_digits = internal::count_digits<4>(abs_value);
writer.write_int(num_digits, get_prefix(), spec, writer.write_int(num_digits, get_prefix(), spec,
@ -1363,7 +1384,7 @@ template <typename Range> class basic_writer {
}; };
void on_bin() { void on_bin() {
if (spec.has(HASH_FLAG)) { if (spec.alt) {
prefix[prefix_size++] = '0'; prefix[prefix_size++] = '0';
prefix[prefix_size++] = static_cast<char>(spec.type); prefix[prefix_size++] = static_cast<char>(spec.type);
} }
@ -1374,7 +1395,7 @@ template <typename Range> class basic_writer {
void on_oct() { void on_oct() {
int num_digits = internal::count_digits<3>(abs_value); int num_digits = internal::count_digits<3>(abs_value);
if (spec.has(HASH_FLAG) && spec.precision <= num_digits) { if (spec.alt && spec.precision <= num_digits) {
// Octal prefix '0' is counted as a digit, so only add it if precision // Octal prefix '0' is counted as a digit, so only add it if precision
// is not greater than the number of digits. // is not greater than the number of digits.
prefix[prefix_size++] = '0'; prefix[prefix_size++] = '0';
@ -1538,18 +1559,18 @@ template <typename Range> class basic_writer {
// Writes a value in the format // Writes a value in the format
// <left-padding><value><right-padding> // <left-padding><value><right-padding>
// where <value> is written by f(it). // where <value> is written by f(it).
template <typename F> void write_padded(const align_spec& spec, F&& f) { template <typename F> void write_padded(const format_specs& spec, F&& f) {
unsigned width = spec.width(); // User-perceived width (in code points). unsigned width = spec.width; // User-perceived width (in code points).
size_t size = f.size(); // The number of code units. size_t size = f.size(); // The number of code units.
size_t num_code_points = width != 0 ? f.width() : size; size_t num_code_points = width != 0 ? f.width() : size;
if (width <= num_code_points) return f(reserve(size)); if (width <= num_code_points) return f(reserve(size));
auto&& it = reserve(width + (size - num_code_points)); auto&& it = reserve(width + (size - num_code_points));
char_type fill = static_cast<char_type>(spec.fill()); char_type fill = spec.fill[0];
std::size_t padding = width - num_code_points; std::size_t padding = width - num_code_points;
if (spec.align() == ALIGN_RIGHT) { if (spec.align == align::right) {
it = std::fill_n(it, padding, fill); it = std::fill_n(it, padding, fill);
f(it); f(it);
} else if (spec.align() == ALIGN_CENTER) { } else if (spec.align == align::center) {
std::size_t left_padding = padding / 2; std::size_t left_padding = padding / 2;
it = std::fill_n(it, left_padding, fill); it = std::fill_n(it, left_padding, fill);
f(it); f(it);
@ -1584,7 +1605,7 @@ template <typename Range> class basic_writer {
FMT_ENABLE_IF(std::is_integral<T>::value)> FMT_ENABLE_IF(std::is_integral<T>::value)>
void write(T value, FormatSpec spec, FormatSpecs... specs) { void write(T value, FormatSpec spec, FormatSpecs... specs) {
format_specs s(spec, specs...); format_specs s(spec, specs...);
s.align_ = ALIGN_RIGHT; s.align = align::right;
write_int(value, s); write_int(value, s);
} }
@ -1635,7 +1656,7 @@ template <typename Range> class basic_writer {
// Writes a formatted string. // Writes a formatted string.
template <typename Char> template <typename Char>
void write(const Char* s, std::size_t size, const align_spec& spec) { void write(const Char* s, std::size_t size, const format_specs& spec) {
write_padded(spec, str_writer<Char>{s, size}); write_padded(spec, str_writer<Char>{s, size});
} }
@ -1650,12 +1671,12 @@ template <typename Range> class basic_writer {
} }
template <typename UIntPtr> template <typename UIntPtr>
void write_pointer(UIntPtr value, const align_spec* spec) { void write_pointer(UIntPtr value, const format_specs* spec) {
int num_digits = internal::count_digits<4>(value); int num_digits = internal::count_digits<4>(value);
auto pw = pointer_writer<UIntPtr>{value, num_digits}; auto pw = pointer_writer<UIntPtr>{value, num_digits};
if (!spec) return pw(reserve(num_digits + 2)); if (!spec) return pw(reserve(num_digits + 2));
align_spec as = *spec; format_specs as = *spec;
if (as.align() == ALIGN_DEFAULT) as.align_ = ALIGN_RIGHT; if (as.align == align::none) as.align = align::right;
write_padded(as, pw); write_padded(as, pw);
} }
}; };
@ -1907,19 +1928,19 @@ template <typename Char> class specs_setter {
FMT_CONSTEXPR specs_setter(const specs_setter& other) FMT_CONSTEXPR specs_setter(const specs_setter& other)
: specs_(other.specs_) {} : specs_(other.specs_) {}
FMT_CONSTEXPR void on_align(alignment align) { specs_.align_ = align; } FMT_CONSTEXPR void on_align(align_t align) { specs_.align = align; }
FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill_ = fill; } FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill[0] = fill; }
FMT_CONSTEXPR void on_plus() { specs_.flags |= SIGN_FLAG | PLUS_FLAG; } FMT_CONSTEXPR void on_plus() { specs_.sign = sign::plus; }
FMT_CONSTEXPR void on_minus() { specs_.flags |= MINUS_FLAG; } FMT_CONSTEXPR void on_minus() { specs_.sign = sign::minus; }
FMT_CONSTEXPR void on_space() { specs_.flags |= SIGN_FLAG; } FMT_CONSTEXPR void on_space() { specs_.sign = sign::space; }
FMT_CONSTEXPR void on_hash() { specs_.flags |= HASH_FLAG; } FMT_CONSTEXPR void on_hash() { specs_.alt = true; }
FMT_CONSTEXPR void on_zero() { FMT_CONSTEXPR void on_zero() {
specs_.align_ = ALIGN_NUMERIC; specs_.align = align::numeric;
specs_.fill_ = '0'; specs_.fill[0] = Char('0');
} }
FMT_CONSTEXPR void on_width(unsigned width) { specs_.width_ = width; } FMT_CONSTEXPR void on_width(unsigned width) { specs_.width = width; }
FMT_CONSTEXPR void on_precision(unsigned precision) { FMT_CONSTEXPR void on_precision(unsigned precision) {
specs_.precision = static_cast<int>(precision); specs_.precision = static_cast<int>(precision);
} }
@ -1971,8 +1992,8 @@ template <typename Handler> class specs_checker : public Handler {
FMT_CONSTEXPR specs_checker(const specs_checker& other) FMT_CONSTEXPR specs_checker(const specs_checker& other)
: Handler(other), checker_(*this, other.arg_type_) {} : Handler(other), checker_(*this, other.arg_type_) {}
FMT_CONSTEXPR void on_align(alignment align) { FMT_CONSTEXPR void on_align(align_t align) {
if (align == ALIGN_NUMERIC) checker_.require_numeric_argument(); if (align == align::numeric) checker_.require_numeric_argument();
Handler::on_align(align); Handler::on_align(align);
} }
@ -2039,7 +2060,7 @@ class specs_handler : public specs_setter<typename Context::char_type> {
context_(ctx) {} context_(ctx) {}
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
set_dynamic_spec<width_checker>(this->specs_.width_, get_arg(arg_id), set_dynamic_spec<width_checker>(this->specs_.width, get_arg(arg_id),
context_.error_handler()); context_.error_handler());
} }
@ -2242,25 +2263,25 @@ template <typename Char, typename Handler>
FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end, FMT_CONSTEXPR const Char* parse_align(const Char* begin, const Char* end,
Handler&& handler) { Handler&& handler) {
FMT_ASSERT(begin != end, ""); FMT_ASSERT(begin != end, "");
alignment align = ALIGN_DEFAULT; auto align = align::none;
int i = 0; int i = 0;
if (begin + 1 != end) ++i; if (begin + 1 != end) ++i;
do { do {
switch (static_cast<char>(begin[i])) { switch (static_cast<char>(begin[i])) {
case '<': case '<':
align = ALIGN_LEFT; align = align::left;
break; break;
case '>': case '>':
align = ALIGN_RIGHT; align = align::right;
break; break;
case '=': case '=':
align = ALIGN_NUMERIC; align = align::numeric;
break; break;
case '^': case '^':
align = ALIGN_CENTER; align = align::center;
break; break;
} }
if (align != ALIGN_DEFAULT) { if (align != align::none) {
if (i > 0) { if (i > 0) {
auto c = *begin; auto c = *begin;
if (c == '{') if (c == '{')
@ -2715,8 +2736,11 @@ void internal::basic_writer<Range>::write_double(T value,
if (std::signbit(value)) { if (std::signbit(value)) {
sign = '-'; sign = '-';
value = -value; value = -value;
} else if (spec.has(SIGN_FLAG)) { } else if (spec.sign != sign::none) {
sign = spec.has(PLUS_FLAG) ? '+' : ' '; if (spec.sign == sign::plus)
sign = '+';
else if (spec.sign == sign::space)
sign = ' ';
} }
if (!std::isfinite(value)) { if (!std::isfinite(value)) {
@ -2732,7 +2756,7 @@ void internal::basic_writer<Range>::write_double(T value,
memory_buffer buffer; memory_buffer buffer;
int exp = 0; int exp = 0;
int precision = spec.has_precision() || !spec.type ? spec.precision : 6; int precision = spec.precision >= 0 || !spec.type ? spec.precision : 6;
unsigned options = handler.fixed ? internal::grisu_options::fixed : 0; unsigned options = handler.fixed ? internal::grisu_options::fixed : 0;
bool use_grisu = USE_GRISU && bool use_grisu = USE_GRISU &&
(spec.type != 'a' && spec.type != 'A' && spec.type != 'e' && (spec.type != 'a' && spec.type != 'A' && spec.type != 'e' &&
@ -2747,17 +2771,17 @@ void internal::basic_writer<Range>::write_double(T value,
buffer.push_back('%'); buffer.push_back('%');
--exp; // Adjust decimal place position. --exp; // Adjust decimal place position.
} }
align_spec as = spec; format_specs as = spec;
if (spec.align() == ALIGN_NUMERIC) { if (spec.align == align::numeric) {
if (sign) { if (sign) {
auto&& it = reserve(1); auto&& it = reserve(1);
*it++ = static_cast<char_type>(sign); *it++ = static_cast<char_type>(sign);
sign = 0; sign = 0;
if (as.width_) --as.width_; if (as.width) --as.width;
} }
as.align_ = ALIGN_RIGHT; as.align = align::right;
} else if (spec.align() == ALIGN_DEFAULT) { } else if (spec.align == align::none) {
as.align_ = ALIGN_RIGHT; as.align = align::right;
} }
char_type decimal_point = handler.use_locale char_type decimal_point = handler.use_locale
? internal::decimal_point<char_type>(locale_) ? internal::decimal_point<char_type>(locale_)
@ -2766,8 +2790,8 @@ void internal::basic_writer<Range>::write_double(T value,
auto params = internal::gen_digits_params(); auto params = internal::gen_digits_params();
params.fixed = handler.fixed; params.fixed = handler.fixed;
params.num_digits = precision; params.num_digits = precision;
params.trailing_zeros = (precision != 0 && (handler.fixed || !spec.type)) || params.trailing_zeros =
spec.has(HASH_FLAG); (precision != 0 && (handler.fixed || !spec.type)) || spec.alt;
write_padded(as, grisu_writer(sign, buffer, exp, params, decimal_point)); write_padded(as, grisu_writer(sign, buffer, exp, params, decimal_point));
} else { } else {
write_padded(as, write_padded(as,
@ -2966,7 +2990,7 @@ struct formatter<T, Char,
template <typename FormatContext> template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
internal::handle_dynamic_spec<internal::width_checker>( internal::handle_dynamic_spec<internal::width_checker>(
specs_.width_, specs_.width_ref, ctx, format_str_); specs_.width, specs_.width_ref, ctx, format_str_);
internal::handle_dynamic_spec<internal::precision_checker>( internal::handle_dynamic_spec<internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx, format_str_); specs_.precision, specs_.precision_ref, ctx, format_str_);
using range_type = using range_type =
@ -3031,7 +3055,7 @@ struct formatter<Char[N], Char> : formatter<basic_string_view<Char>, Char> {
template <typename Char = char> class dynamic_formatter { template <typename Char = char> class dynamic_formatter {
private: private:
struct null_handler : internal::error_handler { struct null_handler : internal::error_handler {
void on_align(alignment) {} void on_align(align_t) {}
void on_plus() {} void on_plus() {}
void on_minus() {} void on_minus() {}
void on_space() {} void on_space() {}
@ -3053,16 +3077,22 @@ template <typename Char = char> class dynamic_formatter {
internal::specs_checker<null_handler> checker( internal::specs_checker<null_handler> checker(
null_handler(), null_handler(),
internal::mapped_type_constant<T, FormatContext>::value); internal::mapped_type_constant<T, FormatContext>::value);
checker.on_align(specs_.align()); checker.on_align(specs_.align);
if (specs_.flags == 0) switch (specs_.sign) {
; // Do nothing. case sign::none:
else if (specs_.has(SIGN_FLAG)) break;
specs_.has(PLUS_FLAG) ? checker.on_plus() : checker.on_space(); case sign::plus:
else if (specs_.has(MINUS_FLAG)) checker.on_plus();
break;
case sign::minus:
checker.on_minus(); checker.on_minus();
else if (specs_.has(HASH_FLAG)) break;
checker.on_hash(); case sign::space:
if (specs_.precision != -1) checker.end_precision(); checker.on_space();
break;
}
if (specs_.alt) checker.on_hash();
if (specs_.precision >= 0) checker.end_precision();
typedef internal::output_range<typename FormatContext::iterator, typedef internal::output_range<typename FormatContext::iterator,
typename FormatContext::char_type> typename FormatContext::char_type>
range; range;
@ -3074,7 +3104,7 @@ template <typename Char = char> class dynamic_formatter {
private: private:
template <typename Context> void handle_specs(Context& ctx) { template <typename Context> void handle_specs(Context& ctx) {
internal::handle_dynamic_spec<internal::width_checker>( internal::handle_dynamic_spec<internal::width_checker>(
specs_.width_, specs_.width_ref, ctx, format_str_); specs_.width, specs_.width_ref, ctx, format_str_);
internal::handle_dynamic_spec<internal::precision_checker>( internal::handle_dynamic_spec<internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx, format_str_); specs_.precision, specs_.precision_ref, ctx, format_str_);
} }

View File

@ -293,7 +293,7 @@ class prepared_format {
auto specs = value.spec.parsed_specs; auto specs = value.spec.parsed_specs;
handle_dynamic_spec<internal::width_checker>( handle_dynamic_spec<internal::width_checker>(
specs.width_, specs.width_ref, ctx, format_view.begin()); specs.width, specs.width_ref, ctx, format_view.begin());
handle_dynamic_spec<internal::precision_checker>( handle_dynamic_spec<internal::precision_checker>(
specs.precision, specs.precision_ref, ctx, format_view.begin()); specs.precision, specs.precision_ref, ctx, format_view.begin());
@ -330,21 +330,10 @@ class prepared_format {
internal::type arg_type) const { internal::type arg_type) const {
internal::error_handler h; internal::error_handler h;
numeric_specs_checker<internal::error_handler> checker(h, arg_type); numeric_specs_checker<internal::error_handler> checker(h, arg_type);
if (specs.align_ == ALIGN_NUMERIC) { if (specs.align == align::numeric) checker.require_numeric_argument();
checker.require_numeric_argument(); if (specs.sign != sign::none) checker.check_sign();
} if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
if (specs.has(PLUS_FLAG | MINUS_FLAG | SIGN_FLAG)) {
checker.check_sign();
}
if (specs.has(HASH_FLAG)) {
checker.require_numeric_argument();
}
if (specs.has_precision()) {
checker.check_precision();
}
} }
private: private:

View File

@ -160,7 +160,7 @@ template <typename Char> class printf_width_handler {
unsigned operator()(T value) { unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_t<T>>(value); auto width = static_cast<uint32_or_64_t<T>>(value);
if (internal::is_negative(value)) { if (internal::is_negative(value)) {
spec_.align_ = ALIGN_LEFT; spec_.align = align::left;
width = 0 - width; width = 0 - width;
} }
unsigned int_max = std::numeric_limits<int>::max(); unsigned int_max = std::numeric_limits<int>::max();
@ -248,8 +248,9 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
format_specs& fmt_spec = *this->spec(); format_specs& fmt_spec = *this->spec();
if (fmt_spec.type && fmt_spec.type != 'c') if (fmt_spec.type && fmt_spec.type != 'c')
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
fmt_spec.flags = 0; fmt_spec.sign = sign::none;
fmt_spec.align_ = ALIGN_RIGHT; fmt_spec.alt = false;
fmt_spec.align = align::right;
return base::operator()(value); return base::operator()(value);
} else { } else {
return base::operator()(value); return base::operator()(value);
@ -378,19 +379,19 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& spec,
for (; it != end; ++it) { for (; it != end; ++it) {
switch (*it) { switch (*it) {
case '-': case '-':
spec.align_ = ALIGN_LEFT; spec.align = align::left;
break; break;
case '+': case '+':
spec.flags |= SIGN_FLAG | PLUS_FLAG; spec.sign = sign::plus;
break; break;
case '0': case '0':
spec.fill_ = '0'; spec.fill[0] = '0';
break; break;
case ' ': case ' ':
spec.flags |= SIGN_FLAG; spec.sign = sign::space;
break; break;
case '#': case '#':
spec.flags |= HASH_FLAG; spec.alt = true;
break; break;
default: default:
return; return;
@ -422,11 +423,11 @@ unsigned basic_printf_context<OutputIt, Char>::parse_header(
++it; ++it;
arg_index = value; arg_index = value;
} else { } else {
if (c == '0') spec.fill_ = '0'; if (c == '0') spec.fill[0] = '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.
spec.width_ = value; spec.width = value;
return arg_index; return arg_index;
} }
} }
@ -436,10 +437,10 @@ unsigned basic_printf_context<OutputIt, Char>::parse_header(
if (it != end) { if (it != end) {
if (*it >= '0' && *it <= '9') { if (*it >= '0' && *it <= '9') {
internal::error_handler eh; internal::error_handler eh;
spec.width_ = parse_nonnegative_int(it, end, eh); spec.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
spec.width_ = visit_format_arg( spec.width = visit_format_arg(
internal::printf_width_handler<char_type>(spec), get_arg()); internal::printf_width_handler<char_type>(spec), get_arg());
} }
} }
@ -464,7 +465,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
out = std::copy(start, it - 1, out); out = std::copy(start, it - 1, out);
format_specs spec; format_specs spec;
spec.align_ = ALIGN_RIGHT; spec.align = align::right;
// Parse argument index, flags and width. // Parse argument index, flags and width.
unsigned arg_index = parse_header(it, end, spec); unsigned arg_index = parse_header(it, end, spec);
@ -486,14 +487,13 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
} }
format_arg arg = get_arg(arg_index); format_arg arg = get_arg(arg_index);
if (spec.has(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg)) if (spec.alt && visit_format_arg(internal::is_zero_int(), arg))
spec.flags = static_cast<uint_least8_t>( spec.alt = false;
spec.flags & (~internal::to_unsigned<int>(HASH_FLAG))); if (spec.fill[0] == '0') {
if (spec.fill_ == '0') {
if (arg.is_arithmetic()) if (arg.is_arithmetic())
spec.align_ = ALIGN_NUMERIC; spec.align = align::numeric;
else else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. spec.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
} }
// Parse length and convert the argument to the required type. // Parse length and convert the argument to the required type.

View File

@ -690,7 +690,7 @@ struct formatter {
template <typename FormatContext> template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
fmt::internal::handle_dynamic_spec<fmt::internal::width_checker>( fmt::internal::handle_dynamic_spec<fmt::internal::width_checker>(
specs_.width_, specs_.width_ref, ctx, nullptr); specs_.width, specs_.width_ref, ctx, nullptr);
fmt::internal::handle_dynamic_spec<fmt::internal::precision_checker>( fmt::internal::handle_dynamic_spec<fmt::internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx, nullptr); specs_.precision, specs_.precision_ref, ctx, nullptr);
using range_type = fmt::internal::output_range<typename FormatContext::iterator, using range_type = fmt::internal::output_range<typename FormatContext::iterator,

View File

@ -2103,7 +2103,7 @@ struct test_format_specs_handler {
enum Result { NONE, PLUS, MINUS, SPACE, HASH, ZERO, ERROR }; enum Result { NONE, PLUS, MINUS, SPACE, HASH, ZERO, ERROR };
Result res = NONE; Result res = NONE;
fmt::alignment align_ = fmt::ALIGN_DEFAULT; fmt::align_t align = fmt::align::none;
char fill = 0; char fill = 0;
unsigned width = 0; unsigned width = 0;
fmt::internal::arg_ref<char> width_ref; fmt::internal::arg_ref<char> width_ref;
@ -2117,7 +2117,7 @@ struct test_format_specs_handler {
FMT_CONSTEXPR test_format_specs_handler( FMT_CONSTEXPR test_format_specs_handler(
const test_format_specs_handler& other) const test_format_specs_handler& other)
: res(other.res), : res(other.res),
align_(other.align_), align(other.align),
fill(other.fill), fill(other.fill),
width(other.width), width(other.width),
width_ref(other.width_ref), width_ref(other.width_ref),
@ -2125,7 +2125,7 @@ struct test_format_specs_handler {
precision_ref(other.precision_ref), precision_ref(other.precision_ref),
type(other.type) {} type(other.type) {}
FMT_CONSTEXPR void on_align(fmt::alignment a) { align_ = a; } FMT_CONSTEXPR void on_align(fmt::align_t a) { align = a; }
FMT_CONSTEXPR void on_fill(char f) { fill = f; } FMT_CONSTEXPR void on_fill(char f) { fill = f; }
FMT_CONSTEXPR void on_plus() { res = PLUS; } FMT_CONSTEXPR void on_plus() { res = PLUS; }
FMT_CONSTEXPR void on_minus() { res = MINUS; } FMT_CONSTEXPR void on_minus() { res = MINUS; }
@ -2159,7 +2159,7 @@ FMT_CONSTEXPR test_format_specs_handler parse_test_specs(const char (&s)[N]) {
TEST(FormatTest, ConstexprParseFormatSpecs) { TEST(FormatTest, ConstexprParseFormatSpecs) {
typedef test_format_specs_handler handler; typedef test_format_specs_handler handler;
static_assert(parse_test_specs("<").align_ == fmt::ALIGN_LEFT, ""); static_assert(parse_test_specs("<").align == fmt::align::left, "");
static_assert(parse_test_specs("*^").fill == '*', ""); static_assert(parse_test_specs("*^").fill == '*', "");
static_assert(parse_test_specs("+").res == handler::PLUS, ""); static_assert(parse_test_specs("+").res == handler::PLUS, "");
static_assert(parse_test_specs("-").res == handler::MINUS, ""); static_assert(parse_test_specs("-").res == handler::MINUS, "");
@ -2216,16 +2216,16 @@ FMT_CONSTEXPR fmt::format_specs parse_specs(const char (&s)[N]) {
} }
TEST(FormatTest, ConstexprSpecsHandler) { TEST(FormatTest, ConstexprSpecsHandler) {
static_assert(parse_specs("<").align() == fmt::ALIGN_LEFT, ""); static_assert(parse_specs("<").align == fmt::align::left, "");
static_assert(parse_specs("*^").fill() == '*', ""); static_assert(parse_specs("*^").fill[0] == '*', "");
static_assert(parse_specs("+").has(fmt::PLUS_FLAG), ""); static_assert(parse_specs("+").sign == fmt::sign::plus, "");
static_assert(parse_specs("-").has(fmt::MINUS_FLAG), ""); static_assert(parse_specs("-").sign == fmt::sign::minus, "");
static_assert(parse_specs(" ").has(fmt::SIGN_FLAG), ""); static_assert(parse_specs(" ").sign == fmt::sign::space, "");
static_assert(parse_specs("#").has(fmt::HASH_FLAG), ""); static_assert(parse_specs("#").alt, "");
static_assert(parse_specs("0").align() == fmt::ALIGN_NUMERIC, ""); static_assert(parse_specs("0").align == fmt::align::numeric, "");
static_assert(parse_specs("42").width() == 42, ""); static_assert(parse_specs("42").width == 42, "");
static_assert(parse_specs("{}").width() == 11, ""); static_assert(parse_specs("{}").width == 11, "");
static_assert(parse_specs("{22}").width() == 22, ""); static_assert(parse_specs("{22}").width == 22, "");
static_assert(parse_specs(".42").precision == 42, ""); static_assert(parse_specs(".42").precision == 42, "");
static_assert(parse_specs(".{}").precision == 11, ""); static_assert(parse_specs(".{}").precision == 11, "");
static_assert(parse_specs(".{22}").precision == 22, ""); static_assert(parse_specs(".{22}").precision == 22, "");
@ -2243,14 +2243,14 @@ FMT_CONSTEXPR fmt::internal::dynamic_format_specs<char> parse_dynamic_specs(
} }
TEST(FormatTest, ConstexprDynamicSpecsHandler) { TEST(FormatTest, ConstexprDynamicSpecsHandler) {
static_assert(parse_dynamic_specs("<").align() == fmt::ALIGN_LEFT, ""); static_assert(parse_dynamic_specs("<").align == fmt::align::left, "");
static_assert(parse_dynamic_specs("*^").fill() == '*', ""); static_assert(parse_dynamic_specs("*^").fill[0] == '*', "");
static_assert(parse_dynamic_specs("+").has(fmt::PLUS_FLAG), ""); static_assert(parse_dynamic_specs("+").sign == fmt::sign::plus, "");
static_assert(parse_dynamic_specs("-").has(fmt::MINUS_FLAG), ""); static_assert(parse_dynamic_specs("-").sign == fmt::sign::minus, "");
static_assert(parse_dynamic_specs(" ").has(fmt::SIGN_FLAG), ""); static_assert(parse_dynamic_specs(" ").sign == fmt::sign::space, "");
static_assert(parse_dynamic_specs("#").has(fmt::HASH_FLAG), ""); static_assert(parse_dynamic_specs("#").alt, "");
static_assert(parse_dynamic_specs("0").align() == fmt::ALIGN_NUMERIC, ""); static_assert(parse_dynamic_specs("0").align == fmt::align::numeric, "");
static_assert(parse_dynamic_specs("42").width() == 42, ""); static_assert(parse_dynamic_specs("42").width == 42, "");
static_assert(parse_dynamic_specs("{}").width_ref.val.index == 11, ""); static_assert(parse_dynamic_specs("{}").width_ref.val.index == 11, "");
static_assert(parse_dynamic_specs("{42}").width_ref.val.index == 42, ""); static_assert(parse_dynamic_specs("{42}").width_ref.val.index == 42, "");
static_assert(parse_dynamic_specs(".42").precision == 42, ""); static_assert(parse_dynamic_specs(".42").precision == 42, "");
@ -2269,7 +2269,7 @@ FMT_CONSTEXPR test_format_specs_handler check_specs(const char (&s)[N]) {
TEST(FormatTest, ConstexprSpecsChecker) { TEST(FormatTest, ConstexprSpecsChecker) {
typedef test_format_specs_handler handler; typedef test_format_specs_handler handler;
static_assert(check_specs("<").align_ == fmt::ALIGN_LEFT, ""); static_assert(check_specs("<").align == fmt::align::left, "");
static_assert(check_specs("*^").fill == '*', ""); static_assert(check_specs("*^").fill == '*', "");
static_assert(check_specs("+").res == handler::PLUS, ""); static_assert(check_specs("+").res == handler::PLUS, "");
static_assert(check_specs("-").res == handler::MINUS, ""); static_assert(check_specs("-").res == handler::MINUS, "");

View File

@ -71,12 +71,12 @@ bool operator==(const format_part<char>::specification& lhs,
} break; } break;
} }
return std::tie(lhs.parsed_specs.width_, lhs.parsed_specs.fill_, return std::tie(lhs.parsed_specs.width, lhs.parsed_specs.fill[0],
lhs.parsed_specs.align_, lhs.parsed_specs.precision, lhs.parsed_specs.align, lhs.parsed_specs.precision,
lhs.parsed_specs.flags, lhs.parsed_specs.type) == lhs.parsed_specs.sign, lhs.parsed_specs.type) ==
std::tie(rhs.parsed_specs.width_, rhs.parsed_specs.fill_, std::tie(rhs.parsed_specs.width, rhs.parsed_specs.fill[0],
rhs.parsed_specs.align_, rhs.parsed_specs.precision, rhs.parsed_specs.align, rhs.parsed_specs.precision,
rhs.parsed_specs.flags, rhs.parsed_specs.type); rhs.parsed_specs.sign, rhs.parsed_specs.type);
} }
bool operator!=(const format_part<char>::specification& lhs, bool operator!=(const format_part<char>::specification& lhs,
@ -352,8 +352,8 @@ TEST(
const auto last_part = format_part(0u); const auto last_part = format_part(0u);
format_part::specification expected_specification(0u); format_part::specification expected_specification(0u);
fmt::internal::dynamic_format_specs<char> specs{}; fmt::internal::dynamic_format_specs<char> specs{};
specs.align_ = fmt::alignment::ALIGN_LEFT; specs.align = fmt::align::left;
specs.width_ = 10; specs.width = 10;
expected_specification.parsed_specs = specs; expected_specification.parsed_specs = specs;
auto expected_substitution_part = format_part(expected_specification); auto expected_substitution_part = format_part(expected_specification);
@ -384,8 +384,8 @@ TEST(
const auto last_part = format_part(format_part::named_argument_id(arg_id)); const auto last_part = format_part(format_part::named_argument_id(arg_id));
format_part::specification expected_specification(arg_id); format_part::specification expected_specification(arg_id);
fmt::internal::dynamic_format_specs<char> specs{}; fmt::internal::dynamic_format_specs<char> specs{};
specs.align_ = fmt::alignment::ALIGN_LEFT; specs.align = fmt::align::left;
specs.width_ = 10; specs.width = 10;
expected_specification.parsed_specs = specs; expected_specification.parsed_specs = specs;
auto expected_substitution_part = format_part(expected_specification); auto expected_substitution_part = format_part(expected_specification);