Implement 'hh' length specifier in printf.

This commit is contained in:
Victor Zverovich 2014-07-30 08:39:07 -07:00
parent 39b0930aee
commit f4156b57f0
3 changed files with 31 additions and 18 deletions

View File

@ -207,23 +207,34 @@ class PrecisionHandler :
} }
}; };
// MakeUnsigned<T>::Type gives an unsigned type corresponding to integer type T.
template <typename T>
struct MakeUnsigned { typedef T Type; };
template <>
struct MakeUnsigned<signed char> { typedef unsigned char Type; };
template <>
struct MakeUnsigned<short> { typedef unsigned short Type; };
// Converts an integer argument to type T. // Converts an integer argument to type T.
template <typename T> template <typename T>
class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> { class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> {
private: private:
fmt::internal::Arg &arg_; fmt::internal::Arg &arg_;
char type_;
public: public:
explicit ArgConverter(fmt::internal::Arg &arg) : arg_(arg) {} ArgConverter(fmt::internal::Arg &arg, char type) : arg_(arg), type_(type) {}
template <typename U> template <typename U>
void visit_any_int(U value) { void visit_any_int(U value) {
if (std::numeric_limits<T>::is_signed) { if (type_ == 'd' || type_ == 'i') {
arg_.type = fmt::internal::Arg::INT; arg_.type = fmt::internal::Arg::INT;
arg_.int_value = static_cast<T>(value); arg_.int_value = static_cast<T>(value);
} else { } else {
arg_.type = fmt::internal::Arg::UINT; arg_.type = fmt::internal::Arg::UINT;
arg_.uint_value = static_cast<T>(value); arg_.uint_value = static_cast<typename MakeUnsigned<T>::Type>(value);
} }
} }
}; };
@ -890,16 +901,14 @@ void fmt::internal::PrintfFormatter<Char>::format(
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
} }
// Parse length. // Parse length and convert the argument to the required type.
switch (*s) { switch (*s) {
case 'h': { case 'h': {
++s; ++s;
// TODO: handle 'hh' if (*s == 'h')
char type = *s; ArgConverter<signed char>(arg, *++s).visit(arg);
if (type == 'd' || type == 'i')
ArgConverter<short>(arg).visit(arg);
else else
ArgConverter<unsigned short>(arg).visit(arg); ArgConverter<short>(arg, *s).visit(arg);
break; break;
} }
case 'l': case 'l':
@ -918,8 +927,10 @@ void fmt::internal::PrintfFormatter<Char>::format(
if (error_) if (error_)
throw FormatError(error_); throw FormatError(error_);
spec.type_ = static_cast<char>(*s++); spec.type_ = static_cast<char>(*s++);
if (arg.type <= Arg::LAST_INTEGER_TYPE && spec.type_ == 'u') if (arg.type <= Arg::LAST_INTEGER_TYPE &&
(spec.type_ == 'i' || spec.type_ == 'u')) {
spec.type_ = 'd'; spec.type_ = 'd';
}
start = s; start = s;

View File

@ -740,6 +740,9 @@ class ArgVisitor {
Result visit_ulong_long(ULongLong value) { Result visit_ulong_long(ULongLong value) {
return FMT_DISPATCH(visit_any_int(value)); return FMT_DISPATCH(visit_any_int(value));
} }
Result visit_char(int value) {
return FMT_DISPATCH(visit_any_int(value));
}
template <typename T> template <typename T>
Result visit_any_int(T) { Result visit_any_int(T) {
return FMT_DISPATCH(visit_unhandled_arg()); return FMT_DISPATCH(visit_unhandled_arg());
@ -756,9 +759,6 @@ class ArgVisitor {
return FMT_DISPATCH(visit_unhandled_arg()); return FMT_DISPATCH(visit_unhandled_arg());
} }
Result visit_char(int) {
return FMT_DISPATCH(visit_unhandled_arg());
}
Result visit_string(Arg::StringValue<char>) { Result visit_string(Arg::StringValue<char>) {
return FMT_DISPATCH(visit_unhandled_arg()); return FMT_DISPATCH(visit_unhandled_arg());
} }

View File

@ -274,17 +274,17 @@ TEST(PrintfTest, DynamicPrecision) {
#define EXPECT_STD_PRINTF(format, arg) { \ #define EXPECT_STD_PRINTF(format, arg) { \
char buffer[BUFFER_SIZE]; \ char buffer[BUFFER_SIZE]; \
safe_sprintf(buffer, "%hd", arg); \ safe_sprintf(buffer, fmt::StringRef(format).c_str(), arg); \
EXPECT_PRINTF(buffer, "%hd", arg); \ EXPECT_PRINTF(buffer, format, arg); \
} }
template <typename T> template <typename T>
void TestLength(const char *length_spec) { void TestLength(const char *length_spec) {
EXPECT_STD_PRINTF(format, 42);
T min = std::numeric_limits<T>::min(), max = std::numeric_limits<T>::max(); T min = std::numeric_limits<T>::min(), max = std::numeric_limits<T>::max();
const char types[] = {'d', 'i', 'u', 'o', 'x', 'X'}; const char types[] = {'d', 'i', 'u', 'o', 'x', 'X'};
for (int i = 0; i < sizeof(types); ++i) { for (int i = 0; i < sizeof(types); ++i) {
std::string format = fmt::format("%{}{}", length_spec, types[i]); std::string format = fmt::format("%{}{}", length_spec, types[i]);
//EXPECT_STD_PRINTF(format, 42);
EXPECT_STD_PRINTF(format, min); EXPECT_STD_PRINTF(format, min);
EXPECT_STD_PRINTF(format, max); EXPECT_STD_PRINTF(format, max);
EXPECT_STD_PRINTF(format, fmt::LongLong(min) - 1); EXPECT_STD_PRINTF(format, fmt::LongLong(min) - 1);
@ -300,9 +300,11 @@ void TestLength(const char *length_spec) {
} }
} }
TEST(PrintfTest, ShortLength) { TEST(PrintfTest, Length) {
TestLength<short>("h"); TestLength<short>("h");
TestLength<unsigned short>("h"); TestLength<unsigned short>("h");
TestLength<signed char>("hh");
TestLength<unsigned char>("hh");
// TODO: more tests // TODO: more tests
} }