mirror of
https://github.com/fmtlib/fmt.git
synced 2025-02-04 15:40:07 +00:00
Improve handling of signed types
This commit is contained in:
parent
32190859ec
commit
ffb9b1d13c
@ -592,6 +592,10 @@ enum class type {
|
||||
custom_type
|
||||
};
|
||||
|
||||
constexpr auto has_sign(type t) -> bool {
|
||||
return ((0xe2a >> static_cast<int>(t)) & 1) != 0;
|
||||
}
|
||||
|
||||
// Maps core type T to the corresponding type enum constant.
|
||||
template <typename T, typename Char>
|
||||
struct type_constant : std::integral_constant<type, type::custom_type> {};
|
||||
@ -2434,7 +2438,7 @@ FMT_CONSTEXPR inline auto parse_presentation_type(char type)
|
||||
case '?':
|
||||
return presentation_type::debug;
|
||||
default:
|
||||
throw_format_error("invalid type specifier");
|
||||
throw_format_error("invalid format specifier");
|
||||
return presentation_type::none;
|
||||
}
|
||||
}
|
||||
@ -2465,31 +2469,25 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
|
||||
begin = align.end;
|
||||
if (begin == end) return begin;
|
||||
|
||||
// Parse sign.
|
||||
switch (to_ascii(*begin)) {
|
||||
case '+':
|
||||
specs.sign = sign::plus;
|
||||
++begin;
|
||||
break;
|
||||
case '-':
|
||||
specs.sign = sign::minus;
|
||||
++begin;
|
||||
break;
|
||||
case ' ':
|
||||
specs.sign = sign::space;
|
||||
++begin;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
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) {
|
||||
throw_format_error("format specifier requires signed argument");
|
||||
if (has_sign(arg_type)) { // Parse sign.
|
||||
switch (to_ascii(*begin)) {
|
||||
case '+':
|
||||
specs.sign = sign::plus;
|
||||
++begin;
|
||||
break;
|
||||
case '-':
|
||||
specs.sign = sign::minus;
|
||||
++begin;
|
||||
break;
|
||||
case ' ':
|
||||
specs.sign = sign::space;
|
||||
++begin;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (begin == end) return begin;
|
||||
}
|
||||
if (begin == end) return begin;
|
||||
|
||||
if (*begin == '#') {
|
||||
require_numeric_argument(arg_type);
|
||||
@ -2649,7 +2647,7 @@ template <typename ErrorHandler>
|
||||
FMT_CONSTEXPR void check_int_type_spec(presentation_type type,
|
||||
ErrorHandler&& eh) {
|
||||
if (type > presentation_type::bin_upper && type != presentation_type::chr)
|
||||
eh.on_error("invalid type specifier");
|
||||
eh.on_error("invalid format specifier");
|
||||
}
|
||||
|
||||
// Checks char specs and returns true if the type spec is char (and not int).
|
||||
@ -2723,7 +2721,7 @@ FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs,
|
||||
result.format = float_format::hex;
|
||||
break;
|
||||
default:
|
||||
eh.on_error("invalid type specifier");
|
||||
eh.on_error("invalid format specifier");
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
@ -2735,7 +2733,8 @@ FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type,
|
||||
if (type == presentation_type::none || type == presentation_type::string ||
|
||||
type == presentation_type::debug)
|
||||
return true;
|
||||
if (type != presentation_type::pointer) eh.on_error("invalid type specifier");
|
||||
if (type != presentation_type::pointer)
|
||||
eh.on_error("invalid format specifier");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2744,14 +2743,14 @@ FMT_CONSTEXPR void check_string_type_spec(presentation_type type,
|
||||
ErrorHandler&& eh = {}) {
|
||||
if (type != presentation_type::none && type != presentation_type::string &&
|
||||
type != presentation_type::debug)
|
||||
eh.on_error("invalid type specifier");
|
||||
eh.on_error("invalid format specifier");
|
||||
}
|
||||
|
||||
template <typename ErrorHandler>
|
||||
FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type,
|
||||
ErrorHandler&& eh) {
|
||||
if (type != presentation_type::none && type != presentation_type::pointer)
|
||||
eh.on_error("invalid type specifier");
|
||||
eh.on_error("invalid format specifier");
|
||||
}
|
||||
|
||||
constexpr int invalid_arg_index = -1;
|
||||
|
@ -2150,7 +2150,7 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
|
||||
case presentation_type::chr:
|
||||
return write_char(out, static_cast<Char>(abs_value), specs);
|
||||
default:
|
||||
throw_format_error("invalid type specifier");
|
||||
throw_format_error("invalid format specifier");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
@ -483,6 +483,19 @@ TEST(arg_test, visit_invalid_arg) {
|
||||
fmt::visit_format_arg(visitor, arg);
|
||||
}
|
||||
|
||||
TEST(core_test, has_sign) {
|
||||
using fmt::detail::type;
|
||||
type types_with_sign[] = {type::int_type, type::long_long_type,
|
||||
type::int128_type, type::float_type,
|
||||
type::double_type, type::long_double_type};
|
||||
for (auto t : types_with_sign) EXPECT_TRUE(fmt::detail::has_sign(t));
|
||||
type types_without_sign[] = {type::uint_type, type::ulong_long_type,
|
||||
type::uint128_type, type::bool_type,
|
||||
type::char_type, type::string_type,
|
||||
type::cstring_type, type::custom_type};
|
||||
for (auto t : types_without_sign) EXPECT_FALSE(fmt::detail::has_sign(t));
|
||||
}
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
|
||||
enum class arg_id_result { none, empty, index, name };
|
||||
@ -511,7 +524,7 @@ constexpr test_arg_id_handler parse_arg_id(const char (&s)[N]) {
|
||||
return h;
|
||||
}
|
||||
|
||||
TEST(format_test, constexpr_parse_arg_id) {
|
||||
TEST(core_test, constexpr_parse_arg_id) {
|
||||
static_assert(parse_arg_id(":").res == arg_id_result::empty, "");
|
||||
static_assert(parse_arg_id("}").res == arg_id_result::empty, "");
|
||||
static_assert(parse_arg_id("42:").res == arg_id_result::index, "");
|
||||
|
@ -646,7 +646,7 @@ TEST(format_test, fill) {
|
||||
fmt::format(string_view("{:\0>4}", 6), '*'));
|
||||
EXPECT_EQ("жж42", fmt::format("{0:ж>4}", 42));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{:\x80\x80\x80\x80\x80>}"), 0),
|
||||
format_error, "invalid type specifier");
|
||||
format_error, "invalid format specifier");
|
||||
}
|
||||
|
||||
TEST(format_test, plus_sign) {
|
||||
@ -654,25 +654,25 @@ TEST(format_test, plus_sign) {
|
||||
EXPECT_EQ("-42", fmt::format("{0:+}", -42));
|
||||
EXPECT_EQ("+42", fmt::format("{0:+}", 42));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42u), format_error,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_EQ("+42", fmt::format("{0:+}", 42l));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_EQ("+42", fmt::format("{0:+}", 42ll));
|
||||
#if FMT_USE_INT128
|
||||
EXPECT_EQ("+42", fmt::format("{0:+}", __int128_t(42)));
|
||||
#endif
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ull), format_error,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
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,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), "abc"), format_error,
|
||||
"format specifier requires numeric argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_THROW_MSG(
|
||||
(void)fmt::format(runtime("{0:+}"), reinterpret_cast<void*>(0x42)),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
format_error, "invalid format specifier");
|
||||
}
|
||||
|
||||
TEST(format_test, minus_sign) {
|
||||
@ -680,22 +680,22 @@ TEST(format_test, minus_sign) {
|
||||
EXPECT_EQ("-42", fmt::format("{0:-}", -42));
|
||||
EXPECT_EQ("42", fmt::format("{0:-}", 42));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42u), format_error,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_EQ("42", fmt::format("{0:-}", 42l));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ul), format_error,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_EQ("42", fmt::format("{0:-}", 42ll));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ull), format_error,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
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,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), "abc"), format_error,
|
||||
"format specifier requires numeric argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_THROW_MSG(
|
||||
(void)fmt::format(runtime("{0:-}"), reinterpret_cast<void*>(0x42)),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
format_error, "invalid format specifier");
|
||||
}
|
||||
|
||||
TEST(format_test, space_sign) {
|
||||
@ -703,22 +703,22 @@ TEST(format_test, space_sign) {
|
||||
EXPECT_EQ("-42", fmt::format("{0: }", -42));
|
||||
EXPECT_EQ(" 42", fmt::format("{0: }", 42));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42u), format_error,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_EQ(" 42", fmt::format("{0: }", 42l));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ul), format_error,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_EQ(" 42", fmt::format("{0: }", 42ll));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ull), format_error,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
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,
|
||||
"format specifier requires signed argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), "abc"), format_error,
|
||||
"format specifier requires numeric argument");
|
||||
"invalid format specifier");
|
||||
EXPECT_THROW_MSG(
|
||||
(void)fmt::format(runtime("{0: }"), reinterpret_cast<void*>(0x42)),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
format_error, "invalid format specifier");
|
||||
}
|
||||
|
||||
TEST(format_test, hash_flag) {
|
||||
@ -1172,7 +1172,7 @@ void check_unknown_types(const T& value, const char* types, const char*) {
|
||||
char c = static_cast<char>(i);
|
||||
if (std::strchr(types, c) || std::strchr(special, c) || !c) continue;
|
||||
safe_sprintf(format_str, "{0:10%c}", c);
|
||||
const char* message = "invalid type specifier";
|
||||
const char* message = "invalid format specifier";
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), value),
|
||||
format_error, message)
|
||||
<< format_str << " " << message;
|
||||
@ -1181,7 +1181,7 @@ void check_unknown_types(const T& value, const char* types, const char*) {
|
||||
|
||||
TEST(format_test, format_int) {
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:v"), 42), format_error,
|
||||
"invalid type specifier");
|
||||
"invalid format specifier");
|
||||
check_unknown_types(42, "bBdoxXnLc", "integer");
|
||||
EXPECT_EQ("x", fmt::format("{:c}", static_cast<int>('x')));
|
||||
}
|
||||
@ -1779,7 +1779,7 @@ TEST(format_test, format_examples) {
|
||||
EXPECT_EQ("The answer is 42", fmt::format("The answer is {}", 42));
|
||||
EXPECT_THROW_MSG(
|
||||
(void)fmt::format(runtime("The answer is {:d}"), "forty-two"),
|
||||
format_error, "invalid type specifier");
|
||||
format_error, "invalid format specifier");
|
||||
|
||||
EXPECT_WRITE(
|
||||
stdout, fmt::print("{}", std::numeric_limits<double>::infinity()), "inf");
|
||||
|
@ -86,11 +86,11 @@ TEST(ostream_test, format_specs) {
|
||||
EXPECT_EQ(" def ", fmt::format("{0:^5}", test_string("def")));
|
||||
EXPECT_EQ("def**", fmt::format("{0:*<5}", test_string("def")));
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), test_string()),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
format_error, "invalid format specifier");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), test_string()),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
format_error, "invalid format specifier");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), test_string()),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
format_error, "invalid format specifier");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), test_string()),
|
||||
format_error, "format specifier requires numeric argument");
|
||||
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), test_string()),
|
||||
|
@ -237,7 +237,7 @@ TEST(printf_test, width) {
|
||||
|
||||
// Width cannot be specified twice.
|
||||
EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error,
|
||||
"invalid type specifier");
|
||||
"invalid format specifier");
|
||||
|
||||
EXPECT_THROW_MSG(test_sprintf(format("%{}d", big_num), 42), format_error,
|
||||
"number is too big");
|
||||
|
Loading…
x
Reference in New Issue
Block a user