diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 784b417d..2720f3fc 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -569,17 +569,17 @@ struct gen_digits_params { // Creates digit generation parameters from format specifiers for a number in // the range [pow(10, exp - 1), pow(10, exp) or 0 if exp == 1. gen_digits_params(const core_format_specs &specs, int exp) - : min_digits(specs.precision_ >= 0 ? to_unsigned(specs.precision_) : 6), + : min_digits(specs.precision >= 0 ? to_unsigned(specs.precision) : 6), fixed(false), upper(false), trailing_zeros(false) { - switch (specs.type_) { + switch (specs.type) { case 'G': upper = true; FMT_FALLTHROUGH case '\0': case 'g': - trailing_zeros = (specs.flags_ & HASH_FLAG) != 0; + trailing_zeros = (specs.flags & HASH_FLAG) != 0; if (-4 <= exp && exp < static_cast(min_digits) + 1) { fixed = true; - if (!specs.type_ && trailing_zeros && exp >= 0) + if (!specs.type && trailing_zeros && exp >= 0) min_digits = to_unsigned(exp) + 1; } break; @@ -718,6 +718,50 @@ FMT_FUNC typename std::enable_if::type buf.resize(size); return true; } + +template +void sprintf_format( + Double value, internal::buffer &buffer, core_format_specs spec) { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buffer.capacity() != 0, "empty buffer"); + + // Build format string. + enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg + char format[MAX_FORMAT_SIZE]; + char *format_ptr = format; + *format_ptr++ = '%'; + if (spec.has(HASH_FLAG)) + *format_ptr++ = '#'; + if (spec.precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (std::is_same::value) + *format_ptr++ = 'L'; + *format_ptr++ = spec.type; + *format_ptr = '\0'; + + // Format using snprintf. + char *start = FMT_NULL; + for (;;) { + std::size_t buffer_size = buffer.capacity(); + start = &buffer[0]; + int result = internal::char_traits::format_float( + start, buffer_size, format, spec.precision, value); + if (result >= 0) { + unsigned n = internal::to_unsigned(result); + if (n < buffer.capacity()) { + buffer.resize(n); + break; // The buffer is large enough - continue with formatting. + } + buffer.reserve(n + 1); + } else { + // If result is negative we ask to increase the capacity by at least 1, + // but as std::vector, the buffer grows exponentially. + buffer.reserve(buffer.capacity() + 1); + } + } +} } // namespace internal #if FMT_USE_WINDOWS_H diff --git a/include/fmt/format.h b/include/fmt/format.h index 91350f3f..88f25177 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1151,31 +1151,26 @@ struct align_spec { FMT_CONSTEXPR unsigned width() const { return width_; } FMT_CONSTEXPR wchar_t fill() const { return fill_; } FMT_CONSTEXPR alignment align() const { return align_; } - - int precision() const { return -1; } }; struct core_format_specs { - int precision_; - uint_least8_t flags_; - char type_; + int precision; + uint_least8_t flags; + char type; + + FMT_CONSTEXPR bool has(unsigned f) const { return (flags & f) != 0; } }; // Format specifiers. template -class basic_format_specs : public align_spec, public core_format_specs { - public: +struct basic_format_specs : align_spec, core_format_specs { FMT_CONSTEXPR basic_format_specs( - unsigned width = 0, char type = 0, wchar_t fill = ' ') + unsigned width = 0, char type_ = 0, wchar_t fill = ' ') : align_spec(width, fill) { - precision_ = -1; - flags_ = 0; - type_ = type; + precision = -1; + flags = 0; + type = type_; } - - FMT_CONSTEXPR bool flag(unsigned f) const { return (flags_ & f) != 0; } - FMT_CONSTEXPR int precision() const { return precision_; } - FMT_CONSTEXPR char type() const { return type_; } }; typedef basic_format_specs format_specs; @@ -1203,6 +1198,9 @@ template inline typename std::enable_if::type grisu2_format(Double, buffer &, core_format_specs) { return false; } +template +void sprintf_format(Double, internal::buffer &, core_format_specs); + template struct format_string_traits< S, typename std::enable_if::value>::type>: @@ -1256,8 +1254,8 @@ template FMT_CONSTEXPR void handle_char_specs( const basic_format_specs *specs, Handler &&handler) { if (!specs) return handler.on_char(); - if (specs->type() && specs->type() != 'c') return handler.on_int(); - if (specs->align() == ALIGN_NUMERIC || specs->flag(~0u) != 0) + if (specs->type && specs->type != 'c') return handler.on_int(); + if (specs->align() == ALIGN_NUMERIC || specs->flags != 0) handler.on_error("invalid format specifier for char"); handler.on_char(); } @@ -1405,8 +1403,8 @@ class arg_formatter_base { void write_pointer(const void *p) { format_specs specs = specs_ ? *specs_ : format_specs(); - specs.flags_ = HASH_FLAG; - specs.type_ = 'x'; + specs.flags = HASH_FLAG; + specs.type = 'x'; writer_.write_int(reinterpret_cast(p), specs); } @@ -1443,7 +1441,7 @@ class arg_formatter_base { // MSVC2013 fails to compile separate overloads for bool and char_type so // use std::is_same instead. if (std::is_same::value) { - if (specs_ && specs_->type_) + if (specs_ && specs_->type) return (*this)(value ? 1 : 0); write(value != 0); } else if (std::is_same::value) { @@ -1492,14 +1490,14 @@ class arg_formatter_base { iterator operator()(const char_type *value) { if (!specs_) return write(value), out(); internal::handle_cstring_type_spec( - specs_->type_, cstring_spec_handler(*this, value)); + specs_->type, cstring_spec_handler(*this, value)); return out(); } iterator operator()(basic_string_view value) { if (specs_) { internal::check_string_type_spec( - specs_->type_, internal::error_handler()); + specs_->type, internal::error_handler()); writer_.write_str(value, *specs_); } else { writer_.write(value); @@ -1509,7 +1507,7 @@ class arg_formatter_base { iterator operator()(const void *value) { if (specs_) - check_pointer_type_spec(specs_->type_, internal::error_handler()); + check_pointer_type_spec(specs_->type, internal::error_handler()); write_pointer(value); return out(); } @@ -1657,10 +1655,10 @@ class specs_setter { FMT_CONSTEXPR void on_align(alignment align) { specs_.align_ = align; } FMT_CONSTEXPR void on_fill(Char fill) { specs_.fill_ = fill; } - FMT_CONSTEXPR void on_plus() { specs_.flags_ |= SIGN_FLAG | PLUS_FLAG; } - FMT_CONSTEXPR void on_minus() { specs_.flags_ |= MINUS_FLAG; } - FMT_CONSTEXPR void on_space() { specs_.flags_ |= SIGN_FLAG; } - FMT_CONSTEXPR void on_hash() { specs_.flags_ |= HASH_FLAG; } + FMT_CONSTEXPR void on_plus() { specs_.flags |= SIGN_FLAG | PLUS_FLAG; } + FMT_CONSTEXPR void on_minus() { specs_.flags |= MINUS_FLAG; } + FMT_CONSTEXPR void on_space() { specs_.flags |= SIGN_FLAG; } + FMT_CONSTEXPR void on_hash() { specs_.flags |= HASH_FLAG; } FMT_CONSTEXPR void on_zero() { specs_.align_ = ALIGN_NUMERIC; @@ -1669,12 +1667,12 @@ class specs_setter { FMT_CONSTEXPR void on_width(unsigned width) { specs_.width_ = width; } FMT_CONSTEXPR void on_precision(unsigned precision) { - specs_.precision_ = static_cast(precision); + specs_.precision = static_cast(precision); } FMT_CONSTEXPR void end_precision() {} FMT_CONSTEXPR void on_type(Char type) { - specs_.type_ = static_cast(type); + specs_.type = static_cast(type); } protected: @@ -1777,7 +1775,7 @@ class specs_handler: public specs_setter { template FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) { set_dynamic_spec( - this->specs_.precision_, get_arg(arg_id), context_.error_handler()); + this->specs_.precision, get_arg(arg_id), context_.error_handler()); } void on_error(const char *message) { @@ -2427,9 +2425,9 @@ class basic_writer { padding = spec.width() - size; size = spec.width(); } - } else if (spec.precision() > static_cast(num_digits)) { - size = prefix.size() + static_cast(spec.precision()); - padding = static_cast(spec.precision()) - num_digits; + } else if (spec.precision > static_cast(num_digits)) { + size = prefix.size() + internal::to_unsigned(spec.precision); + padding = internal::to_unsigned(spec.precision) - num_digits; fill = static_cast('0'); } align_spec as = spec; @@ -2484,8 +2482,8 @@ class basic_writer { prefix[0] = '-'; ++prefix_size; abs_value = 0 - abs_value; - } else if (spec.flag(SIGN_FLAG)) { - prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; + } else if (spec.has(SIGN_FLAG)) { + prefix[0] = spec.has(PLUS_FLAG) ? '+' : ' '; ++prefix_size; } } @@ -2513,14 +2511,14 @@ class basic_writer { template void operator()(It &&it) const { it = internal::format_uint<4, char_type>( - it, self.abs_value, num_digits, self.spec.type() != 'x'); + it, self.abs_value, num_digits, self.spec.type != 'x'); } }; void on_hex() { - if (spec.flag(HASH_FLAG)) { + if (spec.has(HASH_FLAG)) { prefix[prefix_size++] = '0'; - prefix[prefix_size++] = static_cast(spec.type()); + prefix[prefix_size++] = static_cast(spec.type); } unsigned num_digits = count_digits<4>(); writer.write_int(num_digits, get_prefix(), spec, @@ -2539,9 +2537,9 @@ class basic_writer { }; void on_bin() { - if (spec.flag(HASH_FLAG)) { + if (spec.has(HASH_FLAG)) { prefix[prefix_size++] = '0'; - prefix[prefix_size++] = static_cast(spec.type()); + prefix[prefix_size++] = static_cast(spec.type); } unsigned num_digits = count_digits<1>(); writer.write_int(num_digits, get_prefix(), spec, @@ -2550,8 +2548,8 @@ class basic_writer { void on_oct() { unsigned num_digits = count_digits<3>(); - if (spec.flag(HASH_FLAG) && - spec.precision() <= static_cast(num_digits)) { + if (spec.has(HASH_FLAG) && + spec.precision <= static_cast(num_digits)) { // Octal prefix '0' is counted as a digit, so only add it if precision // is not greater than the number of digits. prefix[prefix_size++] = '0'; @@ -2591,7 +2589,7 @@ class basic_writer { // Writes a formatted integer. template void write_int(T value, const Spec &spec) { - internal::handle_int_type_spec(spec.type(), + internal::handle_int_type_spec(spec.type, int_writer(*this, value, spec)); } @@ -2636,9 +2634,6 @@ class basic_writer { // Formats a floating-point number (double or long double). template void write_double(T value, const format_specs &spec); - template - void write_double_sprintf(T value, const format_specs &spec, - internal::buffer &buffer); template struct str_writer { @@ -2740,8 +2735,8 @@ class basic_writer { typename std::enable_if::value>::type write(const T *p) { format_specs specs; - specs.flags_ = HASH_FLAG; - specs.type_ = 'x'; + specs.flags = HASH_FLAG; + specs.type = 'x'; write_int(reinterpret_cast(p), specs); } }; @@ -2777,9 +2772,8 @@ void basic_writer::write_str( basic_string_view s, const format_specs &spec) { const Char *data = s.data(); std::size_t size = s.size(); - std::size_t precision = static_cast(spec.precision_); - if (spec.precision_ >= 0 && precision < size) - size = precision; + if (spec.precision >= 0 && internal::to_unsigned(spec.precision) < size) + size = internal::to_unsigned(spec.precision); write_str(data, size, spec); } @@ -2825,7 +2819,7 @@ template template void basic_writer::write_double(T value, const format_specs &spec) { // Check type. - float_spec_handler handler(static_cast(spec.type())); + float_spec_handler handler(static_cast(spec.type)); internal::handle_float_type_spec(handler.type, handler); char sign = 0; @@ -2834,8 +2828,8 @@ void basic_writer::write_double(T value, const format_specs &spec) { if (std::signbit(value)) { sign = '-'; value = -value; - } else if (spec.flag(SIGN_FLAG)) { - sign = spec.flag(PLUS_FLAG) ? '+' : ' '; + } else if (spec.has(SIGN_FLAG)) { + sign = spec.has(PLUS_FLAG) ? '+' : ' '; } struct write_inf_or_nan_t { @@ -2856,12 +2850,12 @@ void basic_writer::write_double(T value, const format_specs &spec) { memory_buffer buffer; bool use_grisu = internal::use_grisu() && sizeof(T) <= sizeof(double) && - spec.type() != 'a' && spec.type() != 'A' && + spec.type != 'a' && spec.type != 'A' && internal::grisu2_format(static_cast(value), buffer, spec); if (!use_grisu) { format_specs normalized_spec(spec); - normalized_spec.type_ = handler.type; - write_double_sprintf(value, normalized_spec, buffer); + normalized_spec.type = handler.type; + internal::sprintf_format(value, buffer, normalized_spec); } size_t n = buffer.size(); align_spec as = spec; @@ -2883,51 +2877,6 @@ void basic_writer::write_double(T value, const format_specs &spec) { write_padded(as, double_writer{n, sign, buffer}); } -template -template -void basic_writer::write_double_sprintf( - T value, const format_specs &spec, internal::buffer &buffer) { - // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. - FMT_ASSERT(buffer.capacity() != 0, "empty buffer"); - - // Build format string. - enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg - char format[MAX_FORMAT_SIZE]; - char *format_ptr = format; - *format_ptr++ = '%'; - if (spec.flag(HASH_FLAG)) - *format_ptr++ = '#'; - if (spec.precision() >= 0) { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - if (std::is_same::value) - *format_ptr++ = 'L'; - *format_ptr++ = spec.type(); - *format_ptr = '\0'; - - // Format using snprintf. - char *start = FMT_NULL; - for (;;) { - std::size_t buffer_size = buffer.capacity(); - start = &buffer[0]; - int result = internal::char_traits::format_float( - start, buffer_size, format, spec.precision(), value); - if (result >= 0) { - unsigned n = internal::to_unsigned(result); - if (n < buffer.capacity()) { - buffer.resize(n); - break; // The buffer is large enough - continue with formatting. - } - buffer.reserve(n + 1); - } else { - // If result is negative we ask to increase the capacity by at least 1, - // but as std::vector, the buffer grows exponentially. - buffer.reserve(buffer.capacity() + 1); - } - } -} - // Reports a system error without throwing an exception. // Can be used to report errors from destructors. FMT_API void report_system_error(int error_code, @@ -3103,7 +3052,7 @@ struct formatter< internal::specs_checker handler(handler_type(specs_, ctx), type); it = parse_format_specs(it, handler); - auto type_spec = specs_.type(); + auto type_spec = specs_.type; auto eh = ctx.error_handler(); switch (type) { case internal::none_type: @@ -3151,7 +3100,7 @@ struct formatter< internal::handle_dynamic_spec( specs_.width_, specs_.width_ref, ctx); internal::handle_dynamic_spec( - specs_.precision_, specs_.precision_ref, ctx); + specs_.precision, specs_.precision_ref, ctx); typedef output_range range_type; return visit_format_arg(arg_formatter(ctx, &specs_), @@ -3199,19 +3148,14 @@ class dynamic_formatter { internal::specs_checker checker(null_handler(), internal::get_type::value); checker.on_align(specs_.align()); - if (specs_.flags_ == 0) { - // Do nothing. - } else if (specs_.flag(SIGN_FLAG)) { - if (specs_.flag(PLUS_FLAG)) - checker.on_plus(); - else - checker.on_space(); - } else if (specs_.flag(MINUS_FLAG)) { + if (specs_.flags == 0); // Do nothing. + else if (specs_.has(SIGN_FLAG)) + specs_.has(PLUS_FLAG) ? checker.on_plus() : checker.on_space(); + else if (specs_.has(MINUS_FLAG)) checker.on_minus(); - } else if (specs_.flag(HASH_FLAG)) { + else if (specs_.has(HASH_FLAG)) checker.on_hash(); - } - if (specs_.precision_ != -1) + if (specs_.precision != -1) checker.end_precision(); typedef output_range range; @@ -3226,7 +3170,7 @@ class dynamic_formatter { internal::handle_dynamic_spec( specs_.width_, specs_.width_ref, ctx); internal::handle_dynamic_spec( - specs_.precision_, specs_.precision_ref, ctx); + specs_.precision, specs_.precision_ref, ctx); } internal::dynamic_format_specs specs_; diff --git a/include/fmt/printf.h b/include/fmt/printf.h index bffd573c..d58fe9f3 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -222,12 +222,12 @@ class printf_arg_formatter: context_type &context_; void write_null_pointer(char) { - this->spec()->type_ = 0; + this->spec()->type = 0; this->write("(nil)"); } void write_null_pointer(wchar_t) { - this->spec()->type_ = 0; + this->spec()->type = 0; this->write(L"(nil)"); } @@ -253,15 +253,15 @@ class printf_arg_formatter: // use std::is_same instead. if (std::is_same::value) { format_specs &fmt_spec = *this->spec(); - if (fmt_spec.type_ != 's') + if (fmt_spec.type != 's') return base::operator()(value ? 1 : 0); - fmt_spec.type_ = 0; + fmt_spec.type = 0; this->write(value != 0); } else if (std::is_same::value) { 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(value)); - fmt_spec.flags_ = 0; + fmt_spec.flags = 0; fmt_spec.align_ = ALIGN_RIGHT; return base::operator()(value); } else { @@ -280,7 +280,7 @@ class printf_arg_formatter: iterator operator()(const char *value) { if (value) base::operator()(value); - else if (this->spec()->type_ == 'p') + else if (this->spec()->type == 'p') write_null_pointer(char_type()); else this->write("(null)"); @@ -291,7 +291,7 @@ class printf_arg_formatter: iterator operator()(const wchar_t *value) { if (value) base::operator()(value); - else if (this->spec()->type_ == 'p') + else if (this->spec()->type == 'p') write_null_pointer(char_type()); else this->write(L"(null)"); @@ -310,7 +310,7 @@ class printf_arg_formatter: iterator operator()(const void *value) { if (value) return base::operator()(value); - this->spec()->type_ = 0; + this->spec()->type = 0; write_null_pointer(char_type()); return this->out(); } @@ -394,16 +394,16 @@ void basic_printf_context::parse_flags( spec.align_ = ALIGN_LEFT; break; case '+': - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; + spec.flags |= SIGN_FLAG | PLUS_FLAG; break; case '0': spec.fill_ = '0'; break; case ' ': - spec.flags_ |= SIGN_FLAG; + spec.flags |= SIGN_FLAG; break; case '#': - spec.flags_ |= HASH_FLAG; + spec.flags |= HASH_FLAG; break; default: --it; @@ -486,19 +486,19 @@ void basic_printf_context::format() { ++it; if ('0' <= *it && *it <= '9') { internal::error_handler eh; - spec.precision_ = static_cast(parse_nonnegative_int(it, eh)); + spec.precision = static_cast(parse_nonnegative_int(it, eh)); } else if (*it == '*') { ++it; - spec.precision_ = + spec.precision = visit_format_arg(internal::printf_precision_handler(), get_arg(it)); } else { - spec.precision_ = 0; + spec.precision = 0; } } format_arg arg = get_arg(it, arg_index); - if (spec.flag(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg)) - spec.flags_ &= ~internal::to_unsigned(HASH_FLAG); + if (spec.has(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg)) + spec.flags &= ~internal::to_unsigned(HASH_FLAG); if (spec.fill_ == '0') { if (arg.is_arithmetic()) spec.align_ = ALIGN_NUMERIC; @@ -542,12 +542,12 @@ void basic_printf_context::format() { // Parse type. if (!*it) FMT_THROW(format_error("invalid format string")); - spec.type_ = static_cast(*it++); + spec.type = static_cast(*it++); if (arg.is_integral()) { // Normalize type. - switch (spec.type_) { + switch (spec.type) { case 'i': case 'u': - spec.type_ = 'd'; + spec.type = 'd'; break; case 'c': // TODO: handle wchar_t better? diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index 88336cbe..d529771c 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -26,8 +26,8 @@ class custom_arg_formatter : using base::operator(); iterator operator()(double value) { - // Comparing a float to 0.0 is safe - if (round(value * pow(10, spec()->precision())) == 0.0) + // Comparing a float to 0.0 is safe. + if (round(value * pow(10, spec()->precision)) == 0.0) value = 0; return base::operator()(value); } diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index b20d0e19..63b6c405 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -103,7 +103,6 @@ TEST(FPTest, GetCachedPower) { } TEST(FPTest, Grisu2FormatCompilesWithNonIEEEDouble) { - size_t size = 0; fmt::memory_buffer buf; grisu2_format(4.2f, buf, fmt::core_format_specs()); } diff --git a/test/format-test.cc b/test/format-test.cc index 484fa21d..27d72733 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2182,19 +2182,19 @@ FMT_CONSTEXPR fmt::format_specs parse_specs(const char *s) { } 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("+").flag(fmt::PLUS_FLAG), ""); - static_assert(parse_specs("-").flag(fmt::MINUS_FLAG), ""); - static_assert(parse_specs(" ").flag(fmt::SIGN_FLAG), ""); - static_assert(parse_specs("#").flag(fmt::HASH_FLAG), ""); - static_assert(parse_specs("0").align() == fmt::ALIGN_NUMERIC, ""); + static_assert(parse_specs("+").has(fmt::PLUS_FLAG), ""); + static_assert(parse_specs("-").has(fmt::MINUS_FLAG), ""); + static_assert(parse_specs(" ").has(fmt::SIGN_FLAG), ""); + static_assert(parse_specs("#").has(fmt::HASH_FLAG), ""); + static_assert(parse_specs("0").align == fmt::ALIGN_NUMERIC, ""); static_assert(parse_specs("42").width() == 42, ""); static_assert(parse_specs("{}").width() == 11, ""); static_assert(parse_specs("{0}").width() == 22, ""); - static_assert(parse_specs(".42").precision() == 42, ""); - static_assert(parse_specs(".{}").precision() == 11, ""); - static_assert(parse_specs(".{0}").precision() == 22, ""); + static_assert(parse_specs(".42").precision == 42, ""); + static_assert(parse_specs(".{}").precision == 11, ""); + static_assert(parse_specs(".{0}").precision == 22, ""); static_assert(parse_specs("d").type() == 'd', ""); } @@ -2208,17 +2208,17 @@ FMT_CONSTEXPR fmt::internal::dynamic_format_specs } 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("+").flag(fmt::PLUS_FLAG), ""); - static_assert(parse_dynamic_specs("-").flag(fmt::MINUS_FLAG), ""); - static_assert(parse_dynamic_specs(" ").flag(fmt::SIGN_FLAG), ""); - static_assert(parse_dynamic_specs("#").flag(fmt::HASH_FLAG), ""); - static_assert(parse_dynamic_specs("0").align() == fmt::ALIGN_NUMERIC, ""); + static_assert(parse_dynamic_specs("+").has(fmt::PLUS_FLAG), ""); + static_assert(parse_dynamic_specs("-").has(fmt::MINUS_FLAG), ""); + static_assert(parse_dynamic_specs(" ").has(fmt::SIGN_FLAG), ""); + static_assert(parse_dynamic_specs("#").has(fmt::HASH_FLAG), ""); + static_assert(parse_dynamic_specs("0").align == fmt::ALIGN_NUMERIC, ""); static_assert(parse_dynamic_specs("42").width() == 42, ""); static_assert(parse_dynamic_specs("{}").width_ref.index == 33, ""); static_assert(parse_dynamic_specs("{42}").width_ref.index == 42, ""); - static_assert(parse_dynamic_specs(".42").precision() == 42, ""); + static_assert(parse_dynamic_specs(".42").precision == 42, ""); static_assert(parse_dynamic_specs(".{}").precision_ref.index == 33, ""); static_assert(parse_dynamic_specs(".{42}").precision_ref.index == 42, ""); static_assert(parse_dynamic_specs("d").type() == 'd', "");