From 32190859ec3e95ea1bebf455839f22631d727989 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 30 Dec 2022 08:59:12 -0800 Subject: [PATCH] Fix handling of char --- doc/syntax.rst | 4 ++-- include/fmt/core.h | 3 +-- include/fmt/format.h | 5 ++++- test/format-test.cc | 13 ++++--------- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/doc/syntax.rst b/doc/syntax.rst index 3875cd40..74b64c5a 100644 --- a/doc/syntax.rst +++ b/doc/syntax.rst @@ -109,8 +109,8 @@ Note that unless a minimum field width is defined, the field width will always be the same size as the data to fill it, so that the alignment option has no meaning in this case. -The *sign* option is only valid for number types, and can be one of the -following: +The *sign* option is only valid for floating point and signed integer types, +and can be one of the following: +---------+------------------------------------------------------------+ | Option | Meaning | diff --git a/include/fmt/core.h b/include/fmt/core.h index 9f5ff6a6..92e1a6fe 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -2485,8 +2485,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs( if (specs.sign != sign::none) { require_numeric_argument(arg_type); if (is_integral_type(arg_type) && arg_type != type::int_type && - arg_type != type::long_long_type && arg_type != type::int128_type && - arg_type != type::char_type) { + arg_type != type::long_long_type && arg_type != type::int128_type) { throw_format_error("format specifier requires signed argument"); } } diff --git a/include/fmt/format.h b/include/fmt/format.h index c5212bae..6700b139 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1898,9 +1898,12 @@ template FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs, locale_ref loc = {}) -> OutputIt { + // char is formatted as unsigned char for consistency across platforms. + using unsigned_type = + conditional_t::value, unsigned char, unsigned>; return check_char_specs(specs) ? write_char(out, value, specs) - : write(out, static_cast(value), specs, loc); + : write(out, static_cast(value), specs, loc); } // Data for write_int that doesn't depend on output iterator type. It is used to diff --git a/test/format-test.cc b/test/format-test.cc index 9ec329dc..b5fb9b47 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -666,10 +666,8 @@ TEST(format_test, plus_sign) { "format specifier requires signed argument"); EXPECT_EQ("+42", fmt::format("{0:+}", 42.0)); EXPECT_EQ("+42", fmt::format("{0:+}", 42.0l)); - EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+"), 'c'), format_error, - "missing '}' in format string"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 'c'), format_error, - "invalid format specifier for char"); + "format specifier requires signed argument"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), "abc"), format_error, "format specifier requires numeric argument"); EXPECT_THROW_MSG( @@ -691,10 +689,8 @@ TEST(format_test, minus_sign) { "format specifier requires signed argument"); EXPECT_EQ("42", fmt::format("{0:-}", 42.0)); EXPECT_EQ("42", fmt::format("{0:-}", 42.0l)); - EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-"), 'c'), format_error, - "missing '}' in format string"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 'c'), format_error, - "invalid format specifier for char"); + "format specifier requires signed argument"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), "abc"), format_error, "format specifier requires numeric argument"); EXPECT_THROW_MSG( @@ -716,10 +712,8 @@ TEST(format_test, space_sign) { "format specifier requires signed argument"); EXPECT_EQ(" 42", fmt::format("{0: }", 42.0)); EXPECT_EQ(" 42", fmt::format("{0: }", 42.0l)); - EXPECT_THROW_MSG((void)fmt::format(runtime("{0: "), 'c'), format_error, - "missing '}' in format string"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 'c'), format_error, - "invalid format specifier for char"); + "format specifier requires signed argument"); EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), "abc"), format_error, "format specifier requires numeric argument"); EXPECT_THROW_MSG( @@ -1526,6 +1520,7 @@ TEST(format_test, format_char) { EXPECT_EQ("\n", fmt::format("{}", '\n')); EXPECT_EQ("'\\n'", fmt::format("{:?}", '\n')); + EXPECT_EQ("ff", fmt::format("{:x}", '\xff')); } TEST(format_test, format_volatile_char) {