Implement 'hh' length specifier in printf.
This commit is contained in:
parent
39b0930aee
commit
f4156b57f0
31
format.cc
31
format.cc
@ -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;
|
||||||
|
|
||||||
|
6
format.h
6
format.h
@ -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());
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user