diff --git a/include/fmt/base.h b/include/fmt/base.h index fc0ea7b9..4d0fb703 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -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 struct fill_t { } // namespace detail enum class presentation_type : unsigned char { - none, - dec, // 'd' - 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' - chr, // 'c' - string, // 's' - pointer, // 'p' - debug // '?' + // 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' + bin, // 'b' or 'B' + chr, // 'c' + + // 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,7 +2078,8 @@ template struct format_specs { presentation_type type; align_t align : 4; sign_t sign : 3; - bool alt : 1; // Alternate form ('#'). + bool upper : 1; // An uppercase version e.g. 'X' for 'x'. + bool alt : 1; // Alternate form ('#'). bool localized : 1; detail::fill_t fill; @@ -2076,6 +2089,7 @@ template 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"); diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index eccda374..09cce20f 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -1735,8 +1735,8 @@ template OutputIt { auto specs = format_specs(); 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(out, val, specs); } diff --git a/include/fmt/format.h b/include/fmt/format.h index 516857ea..558b6dd1 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -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(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(value), specs); } @@ -2206,34 +2187,21 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg 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 it) { return format_decimal(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 it) { - return format_uint<4, Char>(it, abs_value, num_digits, upper); + return format_uint<4, Char>(it, abs_value, num_digits, specs.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 it) { - return format_uint<1, Char>(it, abs_value, num_digits); - }); - } case presentation_type::oct: { int num_digits = count_digits<3>(abs_value); // Octal prefix '0' is counted as a digit, so only add it if precision @@ -2245,6 +2213,15 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg 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 it) { + return format_uint<1, Char>(it, abs_value, num_digits); + }); + } case presentation_type::chr: return write_char(out, static_cast(abs_value), specs); } @@ -2456,31 +2433,23 @@ FMT_CONSTEXPR auto parse_float_type_spec(const format_specs& 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; diff --git a/include/fmt/printf.h b/include/fmt/printf.h index b7b31cb8..13b4df6c 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -373,7 +373,7 @@ auto parse_header(const Char*& it, const Char* end, format_specs& 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& buf, basic_string_view 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; diff --git a/test/base-test.cc b/test/base-test.cc index f01432ee..98d5b6e0 100644 --- a/test/base-test.cc +++ b/test/base-test.cc @@ -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 { diff --git a/test/scan.h b/test/scan.h index 4c01a840..5fd15baf 100644 --- a/test/scan.h +++ b/test/scan.h @@ -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 ::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); } diff --git a/test/xchar-test.cc b/test/xchar-test.cc index 93461c99..5694880c 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -584,7 +584,7 @@ template struct formatter, 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(), fmt::runtime("{:" + fspecs + "}"), c.real()); auto imag = fmt::format(ctx.locale().template get(),