Improve handling of format specs
This commit is contained in:
parent
c98a5a599f
commit
f9294f0e60
@ -163,6 +163,17 @@
|
||||
# define FMT_CATCH(x) if (false)
|
||||
#endif
|
||||
|
||||
#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
|
||||
# define FMT_FALLTHROUGH [[fallthrough]]
|
||||
#elif defined(__clang__)
|
||||
# define FMT_FALLTHROUGH [[clang::fallthrough]]
|
||||
#elif FMT_GCC_VERSION >= 700 && \
|
||||
(!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
|
||||
# define FMT_FALLTHROUGH [[gnu::fallthrough]]
|
||||
#else
|
||||
# define FMT_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
// Disable [[noreturn]] on MSVC/NVCC because of bogus unreachable code warnings.
|
||||
#if FMT_HAS_CPP_ATTRIBUTE(noreturn) && FMT_EXCEPTIONS && !FMT_MSC_VERSION && \
|
||||
!defined(__NVCC__)
|
||||
@ -2038,25 +2049,26 @@ template <typename Char> struct fill_t {
|
||||
} // namespace detail
|
||||
|
||||
enum class presentation_type : unsigned char {
|
||||
none,
|
||||
dec, // 'd'
|
||||
// Common specifiers:
|
||||
none = 0,
|
||||
debug = 1, // '?'
|
||||
string = 2, // 's' (string, bool)
|
||||
|
||||
// Integral, bool and character specifiers:
|
||||
dec = 3, // 'd'
|
||||
hex, // 'x' or 'X'
|
||||
oct, // 'o'
|
||||
hex_lower, // 'x'
|
||||
hex_upper, // 'X'
|
||||
bin_lower, // 'b'
|
||||
bin_upper, // 'B'
|
||||
hexfloat_lower, // 'a'
|
||||
hexfloat_upper, // 'A'
|
||||
exp_lower, // 'e'
|
||||
exp_upper, // 'E'
|
||||
fixed_lower, // 'f'
|
||||
fixed_upper, // 'F'
|
||||
general_lower, // 'g'
|
||||
general_upper, // 'G'
|
||||
bin, // 'b' or 'B'
|
||||
chr, // 'c'
|
||||
string, // 's'
|
||||
pointer, // 'p'
|
||||
debug // '?'
|
||||
|
||||
// String and pointer specifiers:
|
||||
pointer = 3, // 'p'
|
||||
|
||||
// Floating-point specifiers:
|
||||
exp = 1, // 'e' or 'E' (1 since there is no FP debug presentation)
|
||||
fixed, // 'f' or 'F'
|
||||
general, // 'g' or 'G'
|
||||
hexfloat // 'a' or 'A'
|
||||
};
|
||||
|
||||
// Format specifiers for built-in and string types.
|
||||
@ -2066,6 +2078,7 @@ template <typename Char = char> struct format_specs {
|
||||
presentation_type type;
|
||||
align_t align : 4;
|
||||
sign_t sign : 3;
|
||||
bool upper : 1; // An uppercase version e.g. 'X' for 'x'.
|
||||
bool alt : 1; // Alternate form ('#').
|
||||
bool localized : 1;
|
||||
detail::fill_t<Char> fill;
|
||||
@ -2076,6 +2089,7 @@ template <typename Char = char> struct format_specs {
|
||||
type(presentation_type::none),
|
||||
align(align::none),
|
||||
sign(sign::none),
|
||||
upper(false),
|
||||
alt(false),
|
||||
localized(false) {}
|
||||
};
|
||||
@ -2401,32 +2415,38 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end,
|
||||
break;
|
||||
case 'd':
|
||||
return parse_presentation_type(pres::dec, integral_set);
|
||||
case 'X':
|
||||
specs.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'x':
|
||||
return parse_presentation_type(pres::hex, integral_set);
|
||||
case 'o':
|
||||
return parse_presentation_type(pres::oct, integral_set);
|
||||
case 'x':
|
||||
return parse_presentation_type(pres::hex_lower, integral_set);
|
||||
case 'X':
|
||||
return parse_presentation_type(pres::hex_upper, integral_set);
|
||||
case 'b':
|
||||
return parse_presentation_type(pres::bin_lower, integral_set);
|
||||
case 'B':
|
||||
return parse_presentation_type(pres::bin_upper, integral_set);
|
||||
case 'a':
|
||||
return parse_presentation_type(pres::hexfloat_lower, float_set);
|
||||
case 'A':
|
||||
return parse_presentation_type(pres::hexfloat_upper, float_set);
|
||||
case 'e':
|
||||
return parse_presentation_type(pres::exp_lower, float_set);
|
||||
specs.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'b':
|
||||
return parse_presentation_type(pres::bin, integral_set);
|
||||
case 'E':
|
||||
return parse_presentation_type(pres::exp_upper, float_set);
|
||||
case 'f':
|
||||
return parse_presentation_type(pres::fixed_lower, float_set);
|
||||
specs.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'e':
|
||||
return parse_presentation_type(pres::exp, float_set);
|
||||
case 'F':
|
||||
return parse_presentation_type(pres::fixed_upper, float_set);
|
||||
case 'g':
|
||||
return parse_presentation_type(pres::general_lower, float_set);
|
||||
specs.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'f':
|
||||
return parse_presentation_type(pres::fixed, float_set);
|
||||
case 'G':
|
||||
return parse_presentation_type(pres::general_upper, float_set);
|
||||
specs.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'g':
|
||||
return parse_presentation_type(pres::general, float_set);
|
||||
case 'A':
|
||||
specs.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'a':
|
||||
return parse_presentation_type(pres::hexfloat, float_set);
|
||||
case 'c':
|
||||
if (arg_type == type::bool_type)
|
||||
throw_format_error("invalid format specifier");
|
||||
|
@ -1735,8 +1735,8 @@ template <typename Char, typename Rep, typename OutputIt,
|
||||
auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt {
|
||||
auto specs = format_specs<Char>();
|
||||
specs.precision = precision;
|
||||
specs.type = precision >= 0 ? presentation_type::fixed_lower
|
||||
: presentation_type::general_lower;
|
||||
specs.type =
|
||||
precision >= 0 ? presentation_type::fixed : presentation_type::general;
|
||||
return write<Char>(out, val, specs);
|
||||
}
|
||||
|
||||
|
@ -71,17 +71,6 @@
|
||||
# define FMT_INLINE_VARIABLE
|
||||
#endif
|
||||
|
||||
#if FMT_HAS_CPP17_ATTRIBUTE(fallthrough)
|
||||
# define FMT_FALLTHROUGH [[fallthrough]]
|
||||
#elif defined(__clang__)
|
||||
# define FMT_FALLTHROUGH [[clang::fallthrough]]
|
||||
#elif FMT_GCC_VERSION >= 700 && \
|
||||
(!defined(__EDG_VERSION__) || __EDG_VERSION__ >= 520)
|
||||
# define FMT_FALLTHROUGH [[gnu::fallthrough]]
|
||||
#else
|
||||
# define FMT_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#ifndef FMT_NO_UNIQUE_ADDRESS
|
||||
# if FMT_CPLUSPLUS >= 202002L
|
||||
# if FMT_HAS_CPP_ATTRIBUTE(no_unique_address)
|
||||
@ -2096,30 +2085,17 @@ auto write_int(OutputIt out, UInt value, unsigned prefix,
|
||||
FMT_ASSERT(false, "");
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::none:
|
||||
case presentation_type::dec: {
|
||||
case presentation_type::dec:
|
||||
num_digits = count_digits(value);
|
||||
format_decimal<char>(appender(buffer), value, num_digits);
|
||||
break;
|
||||
}
|
||||
case presentation_type::hex_lower:
|
||||
case presentation_type::hex_upper: {
|
||||
bool upper = specs.type == presentation_type::hex_upper;
|
||||
case presentation_type::hex:
|
||||
if (specs.alt)
|
||||
prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0');
|
||||
prefix_append(prefix, unsigned(specs.upper ? 'X' : 'x') << 8 | '0');
|
||||
num_digits = count_digits<4>(value);
|
||||
format_uint<4, char>(appender(buffer), value, num_digits, upper);
|
||||
format_uint<4, char>(appender(buffer), value, num_digits, specs.upper);
|
||||
break;
|
||||
}
|
||||
case presentation_type::bin_lower:
|
||||
case presentation_type::bin_upper: {
|
||||
bool upper = specs.type == presentation_type::bin_upper;
|
||||
if (specs.alt)
|
||||
prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0');
|
||||
num_digits = count_digits<1>(value);
|
||||
format_uint<1, char>(appender(buffer), value, num_digits);
|
||||
break;
|
||||
}
|
||||
case presentation_type::oct: {
|
||||
case presentation_type::oct:
|
||||
num_digits = count_digits<3>(value);
|
||||
// Octal prefix '0' is counted as a digit, so only add it if precision
|
||||
// is not greater than the number of digits.
|
||||
@ -2127,7 +2103,12 @@ auto write_int(OutputIt out, UInt value, unsigned prefix,
|
||||
prefix_append(prefix, '0');
|
||||
format_uint<3, char>(appender(buffer), value, num_digits);
|
||||
break;
|
||||
}
|
||||
case presentation_type::bin:
|
||||
if (specs.alt)
|
||||
prefix_append(prefix, unsigned(specs.upper ? 'B' : 'b') << 8 | '0');
|
||||
num_digits = count_digits<1>(value);
|
||||
format_uint<1, char>(appender(buffer), value, num_digits);
|
||||
break;
|
||||
case presentation_type::chr:
|
||||
return write_char(out, static_cast<Char>(value), specs);
|
||||
}
|
||||
@ -2206,32 +2187,19 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::none:
|
||||
case presentation_type::dec: {
|
||||
auto num_digits = count_digits(abs_value);
|
||||
int num_digits = count_digits(abs_value);
|
||||
return write_int(
|
||||
out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
|
||||
return format_decimal<Char>(it, abs_value, num_digits).end;
|
||||
});
|
||||
}
|
||||
case presentation_type::hex_lower:
|
||||
case presentation_type::hex_upper: {
|
||||
bool upper = specs.type == presentation_type::hex_upper;
|
||||
case presentation_type::hex: {
|
||||
if (specs.alt)
|
||||
prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0');
|
||||
prefix_append(prefix, unsigned(specs.upper ? 'X' : 'x') << 8 | '0');
|
||||
int num_digits = count_digits<4>(abs_value);
|
||||
return write_int(
|
||||
out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
|
||||
return format_uint<4, Char>(it, abs_value, num_digits, upper);
|
||||
});
|
||||
}
|
||||
case presentation_type::bin_lower:
|
||||
case presentation_type::bin_upper: {
|
||||
bool upper = specs.type == presentation_type::bin_upper;
|
||||
if (specs.alt)
|
||||
prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0');
|
||||
int num_digits = count_digits<1>(abs_value);
|
||||
return write_int(out, num_digits, prefix, specs,
|
||||
[=](reserve_iterator<OutputIt> it) {
|
||||
return format_uint<1, Char>(it, abs_value, num_digits);
|
||||
return format_uint<4, Char>(it, abs_value, num_digits, specs.upper);
|
||||
});
|
||||
}
|
||||
case presentation_type::oct: {
|
||||
@ -2245,6 +2213,15 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
|
||||
return format_uint<3, Char>(it, abs_value, num_digits);
|
||||
});
|
||||
}
|
||||
case presentation_type::bin: {
|
||||
if (specs.alt)
|
||||
prefix_append(prefix, unsigned(specs.upper ? 'B' : 'b') << 8 | '0');
|
||||
int num_digits = count_digits<1>(abs_value);
|
||||
return write_int(out, num_digits, prefix, specs,
|
||||
[=](reserve_iterator<OutputIt> it) {
|
||||
return format_uint<1, Char>(it, abs_value, num_digits);
|
||||
});
|
||||
}
|
||||
case presentation_type::chr:
|
||||
return write_char(out, static_cast<Char>(abs_value), specs);
|
||||
}
|
||||
@ -2456,31 +2433,23 @@ FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs)
|
||||
case presentation_type::none:
|
||||
result.format = float_format::general;
|
||||
break;
|
||||
case presentation_type::general_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::general_lower:
|
||||
case presentation_type::exp:
|
||||
result.format = float_format::exp;
|
||||
result.upper = specs.upper;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case presentation_type::fixed:
|
||||
result.format = float_format::fixed;
|
||||
result.upper = specs.upper;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case presentation_type::general:
|
||||
result.upper = specs.upper;
|
||||
result.format = float_format::general;
|
||||
break;
|
||||
case presentation_type::exp_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::exp_lower:
|
||||
result.format = float_format::exp;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case presentation_type::fixed_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::fixed_lower:
|
||||
result.format = float_format::fixed;
|
||||
result.showpoint |= specs.precision != 0;
|
||||
break;
|
||||
case presentation_type::hexfloat_upper:
|
||||
result.upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case presentation_type::hexfloat_lower:
|
||||
case presentation_type::hexfloat:
|
||||
result.format = float_format::hex;
|
||||
result.upper = specs.upper;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
|
@ -373,7 +373,7 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
|
||||
return arg_index;
|
||||
}
|
||||
|
||||
inline auto parse_printf_presentation_type(char c, type t)
|
||||
inline auto parse_printf_presentation_type(char c, type t, bool& upper)
|
||||
-> presentation_type {
|
||||
using pt = presentation_type;
|
||||
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
|
||||
@ -382,26 +382,31 @@ inline auto parse_printf_presentation_type(char c, type t)
|
||||
return in(t, integral_set) ? pt::dec : pt::none;
|
||||
case 'o':
|
||||
return in(t, integral_set) ? pt::oct : pt::none;
|
||||
case 'x':
|
||||
return in(t, integral_set) ? pt::hex_lower : pt::none;
|
||||
case 'X':
|
||||
return in(t, integral_set) ? pt::hex_upper : pt::none;
|
||||
case 'a':
|
||||
return in(t, float_set) ? pt::hexfloat_lower : pt::none;
|
||||
case 'A':
|
||||
return in(t, float_set) ? pt::hexfloat_upper : pt::none;
|
||||
case 'e':
|
||||
return in(t, float_set) ? pt::exp_lower : pt::none;
|
||||
upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'x':
|
||||
return in(t, integral_set) ? pt::hex : pt::none;
|
||||
case 'E':
|
||||
return in(t, float_set) ? pt::exp_upper : pt::none;
|
||||
case 'f':
|
||||
return in(t, float_set) ? pt::fixed_lower : pt::none;
|
||||
upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'e':
|
||||
return in(t, float_set) ? pt::exp : pt::none;
|
||||
case 'F':
|
||||
return in(t, float_set) ? pt::fixed_upper : pt::none;
|
||||
case 'g':
|
||||
return in(t, float_set) ? pt::general_lower : pt::none;
|
||||
upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'f':
|
||||
return in(t, float_set) ? pt::fixed : pt::none;
|
||||
case 'G':
|
||||
return in(t, float_set) ? pt::general_upper : pt::none;
|
||||
upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'g':
|
||||
return in(t, float_set) ? pt::general : pt::none;
|
||||
case 'A':
|
||||
upper = true;
|
||||
FMT_FALLTHROUGH;
|
||||
case 'a':
|
||||
return in(t, float_set) ? pt::hexfloat : pt::none;
|
||||
case 'c':
|
||||
return in(t, integral_set) ? pt::chr : pt::none;
|
||||
case 's':
|
||||
@ -548,9 +553,11 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
break;
|
||||
}
|
||||
}
|
||||
specs.type = parse_printf_presentation_type(type, arg.type());
|
||||
bool upper = false;
|
||||
specs.type = parse_printf_presentation_type(type, arg.type(), upper);
|
||||
if (specs.type == presentation_type::none)
|
||||
throw_format_error("invalid format specifier");
|
||||
specs.upper = upper;
|
||||
|
||||
start = it;
|
||||
|
||||
|
@ -516,7 +516,7 @@ TEST(core_test, constexpr_parse_format_specs) {
|
||||
static_assert(parse_test_specs(".42").precision == 42, "");
|
||||
static_assert(parse_test_specs(".{42}").precision_ref.val.index == 42, "");
|
||||
static_assert(
|
||||
parse_test_specs("f").type == fmt::presentation_type::fixed_lower, "");
|
||||
parse_test_specs("f").type == fmt::presentation_type::fixed, "");
|
||||
}
|
||||
|
||||
struct test_format_string_handler {
|
||||
|
@ -439,7 +439,7 @@ const char* parse_scan_specs(const char* begin, const char* end,
|
||||
switch (to_ascii(*begin)) {
|
||||
// TODO: parse more scan format specifiers
|
||||
case 'x':
|
||||
specs.type = presentation_type::hex_lower;
|
||||
specs.type = presentation_type::hex;
|
||||
++begin;
|
||||
break;
|
||||
case '}':
|
||||
@ -508,7 +508,7 @@ auto read_hex(scan_iterator it, T& value) -> scan_iterator {
|
||||
template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)>
|
||||
auto read(scan_iterator it, T& value, const format_specs<>& specs)
|
||||
-> scan_iterator {
|
||||
if (specs.type == presentation_type::hex_lower) return read_hex(it, value);
|
||||
if (specs.type == presentation_type::hex) return read_hex(it, value);
|
||||
return read(it, value);
|
||||
}
|
||||
|
||||
|
@ -584,7 +584,7 @@ template <class charT> struct formatter<std::complex<double>, charT> {
|
||||
specs.precision, specs.precision_ref, ctx);
|
||||
auto fspecs = std::string();
|
||||
if (specs.precision > 0) fspecs = fmt::format(".{}", specs.precision);
|
||||
if (specs.type == presentation_type::fixed_lower) fspecs += 'f';
|
||||
if (specs.type == presentation_type::fixed) fspecs += 'f';
|
||||
auto real = fmt::format(ctx.locale().template get<std::locale>(),
|
||||
fmt::runtime("{:" + fspecs + "}"), c.real());
|
||||
auto imag = fmt::format(ctx.locale().template get<std::locale>(),
|
||||
|
Loading…
Reference in New Issue
Block a user