Check floating-point type specifiers

This commit is contained in:
Victor Zverovich 2017-11-23 09:14:37 -08:00
parent 6570dc3122
commit c8a9d902dd
2 changed files with 117 additions and 32 deletions

View File

@ -1771,8 +1771,8 @@ using wparse_context = basic_parse_context<wchar_t>;
namespace internal { namespace internal {
template <typename Handler> template <typename Handler>
constexpr void handle_integral_type_spec(char c, Handler &&handler) { constexpr void handle_int_type_spec(char spec, Handler &&handler) {
switch (c) { switch (spec) {
case 0: case 'd': case 0: case 'd':
handler.on_dec(); handler.on_dec();
break; break;
@ -1793,6 +1793,27 @@ constexpr void handle_integral_type_spec(char c, Handler &&handler) {
} }
} }
template <typename Handler>
constexpr void handle_float_type_spec(char spec, Handler &&handler) {
switch (spec) {
case 0: case 'g': case 'G':
handler.on_general();
break;
case 'e': case 'E':
handler.on_exp();
break;
case 'f': case 'F':
handler.on_fixed();
break;
case 'a': case 'A':
handler.on_hex();
break;
default:
handler.on_error();
break;
}
}
template <typename ErrorHandler> template <typename ErrorHandler>
class int_type_checker : private ErrorHandler { class int_type_checker : private ErrorHandler {
public: public:
@ -1809,6 +1830,21 @@ class int_type_checker : private ErrorHandler {
} }
}; };
template <typename ErrorHandler>
class float_type_checker : private ErrorHandler {
public:
constexpr float_type_checker(ErrorHandler eh) : ErrorHandler(eh) {}
constexpr void on_general() {}
constexpr void on_exp() {}
constexpr void on_fixed() {}
constexpr void on_hex() {}
constexpr void on_error() {
ErrorHandler::on_error("invalid type specifier");
}
};
template <typename Context> template <typename Context>
class arg_map { class arg_map {
private: private:
@ -3256,35 +3292,52 @@ void basic_writer<Char>::write_int(T value, const Spec& spec) {
spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer");
} }
}; };
internal::handle_integral_type_spec( internal::handle_int_type_spec(spec.type(), spec_handler(*this, value, spec));
spec.type(), spec_handler(*this, value, spec));
} }
template <typename Char> template <typename Char>
template <typename T> template <typename T>
void basic_writer<Char>::write_double(T value, const format_specs &spec) { void basic_writer<Char>::write_double(T value, const format_specs &spec) {
// Check type. // Check type.
char type = spec.type(); struct spec_handler {
bool upper = false; char type;
switch (type) { bool upper = false;
case 0:
type = 'g'; explicit spec_handler(char t) : type(t) {}
break;
case 'e': case 'f': case 'g': case 'a': void on_general() {
break; if (type == 'G')
case 'F': upper = true;
else
type = 'g';
}
void on_exp() {
if (type == 'E')
upper = true;
}
void on_fixed() {
if (type == 'F') {
upper = true;
#if FMT_MSC_VER #if FMT_MSC_VER
// MSVC's printf doesn't support 'F'. // MSVC's printf doesn't support 'F'.
type = 'f'; type = 'f';
#endif #endif
// Fall through. }
case 'E': case 'G': case 'A': }
upper = true;
break; void on_hex() {
default: if (type == 'A')
internal::report_unknown_type(type, "double"); upper = true;
break; }
}
void on_error() {
internal::report_unknown_type(type, "double");
}
};
spec_handler handler(spec.type());
internal::handle_float_type_spec(spec.type(), handler);
char sign = 0; char sign = 0;
// Use isnegative instead of value < 0 because the latter is always // Use isnegative instead of value < 0 because the latter is always
@ -3300,7 +3353,7 @@ void basic_writer<Char>::write_double(T value, const format_specs &spec) {
// Format NaN ourselves because sprintf's output is not consistent // Format NaN ourselves because sprintf's output is not consistent
// across platforms. // across platforms.
std::size_t nan_size = 4; std::size_t nan_size = 4;
const char *nan = upper ? " NAN" : " nan"; const char *nan = handler.upper ? " NAN" : " nan";
if (!sign) { if (!sign) {
--nan_size; --nan_size;
++nan; ++nan;
@ -3315,7 +3368,7 @@ void basic_writer<Char>::write_double(T value, const format_specs &spec) {
// Format infinity ourselves because sprintf's output is not consistent // Format infinity ourselves because sprintf's output is not consistent
// across platforms. // across platforms.
std::size_t inf_size = 4; std::size_t inf_size = 4;
const char *inf = upper ? " INF" : " inf"; const char *inf = handler.upper ? " INF" : " inf";
if (!sign) { if (!sign) {
--inf_size; --inf_size;
++inf; ++inf;
@ -3357,7 +3410,7 @@ void basic_writer<Char>::write_double(T value, const format_specs &spec) {
} }
append_float_length(format_ptr, value); append_float_length(format_ptr, value);
*format_ptr++ = type; *format_ptr++ = handler.type;
*format_ptr = '\0'; *format_ptr = '\0';
// Format using snprintf. // Format using snprintf.
@ -3729,13 +3782,43 @@ struct formatter<
constexpr typename ParseContext::iterator parse(ParseContext &ctx) { constexpr typename ParseContext::iterator parse(ParseContext &ctx) {
auto it = internal::null_terminating_iterator<Char>(ctx); auto it = internal::null_terminating_iterator<Char>(ctx);
using handler_type = internal::dynamic_specs_handler<ParseContext>; using handler_type = internal::dynamic_specs_handler<ParseContext>;
auto type = internal::get_type<T>();
internal::specs_checker<handler_type> internal::specs_checker<handler_type>
handler(handler_type(specs_, ctx), internal::get_type<T>()); handler(handler_type(specs_, ctx), type);
it = parse_format_specs(it, handler); it = parse_format_specs(it, handler);
if (std::is_integral<T>::value) { auto eh = ctx.error_handler();
auto eh = ctx.error_handler(); switch (type) {
handle_integral_type_spec( case internal::NONE:
case internal::NAMED_ARG:
FMT_ASSERT(false, "invalid argument type");
break;
case internal::INT:
case internal::UINT:
case internal::LONG_LONG:
case internal::ULONG_LONG:
case internal::BOOL:
handle_int_type_spec(
specs_.type(), internal::int_type_checker<decltype(eh)>(eh)); specs_.type(), internal::int_type_checker<decltype(eh)>(eh));
break;
case internal::CHAR:
// TODO
break;
case internal::DOUBLE:
case internal::LONG_DOUBLE:
handle_float_type_spec(
specs_.type(), internal::float_type_checker<decltype(eh)>(eh));
break;
case internal::CSTRING:
case internal::STRING:
// TODO
break;
case internal::POINTER:
// TODO
break;
case internal::CUSTOM:
// Custom format specifiers should be checked in parse functions of
// formatter specializations.
break;
} }
return pointer_from(it); return pointer_from(it);
} }

View File

@ -1844,8 +1844,8 @@ constexpr size_t len(const char *s) {
} }
constexpr bool equal(const char *s1, const char *s2) { constexpr bool equal(const char *s1, const char *s2) {
if (!s1 && !s2) if (!s1 || !s2)
return true; return s1 == s2;
while (*s1 && *s1 == *s2) { while (*s1 && *s1 == *s2) {
++s1; ++s1;
++s2; ++s2;
@ -1892,6 +1892,8 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{: }", "format specifier requires signed argument", unsigned); EXPECT_ERROR("{: }", "format specifier requires signed argument", unsigned);
EXPECT_ERROR("{:.2}", "precision not allowed for this argument type", int); EXPECT_ERROR("{:.2}", "precision not allowed for this argument type", int);
EXPECT_ERROR("{:s}", "invalid type specifier", int); EXPECT_ERROR("{:s}", "invalid type specifier", int);
EXPECT_ERROR("{:s}", "invalid type specifier", bool);
EXPECT_ERROR("{:s}", "invalid type specifier", double);
#endif #endif
EXPECT_ERROR("{foo", "missing '}' in format string", int); EXPECT_ERROR("{foo", "missing '}' in format string", int);
EXPECT_ERROR("{10000000000}", "number is too big"); EXPECT_ERROR("{10000000000}", "number is too big");