Add ArgVisitor.

This commit is contained in:
Victor Zverovich 2014-07-14 06:55:29 -07:00
parent 979561cc00
commit 591ad0a64a
3 changed files with 263 additions and 90 deletions

View File

@ -50,6 +50,7 @@
# undef ERROR
#endif
using fmt::LongLong;
using fmt::ULongLong;
using fmt::internal::Arg;
@ -167,6 +168,26 @@ const Char *find_closing_brace(const Char *s, int num_open_braces = 1) {
}
throw fmt::FormatError("unmatched '{' in format");
}
// Handles width specifier.
struct WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, ULongLong> {
private:
fmt::FormatSpec &spec_;
public:
explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {}
ULongLong visit_unhandled_arg() {
throw fmt::FormatError("width is not integer");
}
ULongLong visit_any_int(fmt::LongLong value) {
ULongLong width = value;
if (value < 0) {
spec_.align_ = fmt::ALIGN_LEFT;
width = 0 - width;
}
return width;
}
ULongLong visit_any_uint(ULongLong value) { return value; }
};
} // namespace
int fmt::internal::SignBitNoInline(double value) { return SignBit(value); }
@ -657,34 +678,7 @@ unsigned fmt::internal::PrintfParser<Char>::ParseHeader(
spec.width_ = ParseNonnegativeInt(s, error);
} else if (*s == '*') {
++s;
const Arg &arg = HandleArgIndex(UINT_MAX, error);
// TODO: use ArgVisitor
ULongLong width = 0;
switch (arg.type) {
case Arg::INT:
width = arg.int_value;
if (arg.int_value < 0) {
spec.align_ = ALIGN_LEFT;
width = 0 - width;
}
break;
case Arg::UINT:
width = arg.uint_value;
break;
case Arg::LONG_LONG:
width = arg.long_long_value;
if (arg.long_long_value < 0) {
spec.align_ = ALIGN_LEFT;
width = 0 - width;
}
break;
case Arg::ULONG_LONG:
width = arg.ulong_long_value;
break;
default:
if (!error)
error = "width is not integer";
}
ULongLong width = WidthHandler(spec).visit(HandleArgIndex(UINT_MAX, error));
if (width <= INT_MAX)
spec.width_ = static_cast<unsigned>(width);
else if (!error)

100
format.h
View File

@ -717,6 +717,106 @@ public:
}
};
#define FMT_DISPATCH(call) static_cast<Impl*>(this)->call
// An argument visitor.
// To use ArgVisitor define a subclass that implements some or all of the
// visit methods with the same signatures as the methods in ArgVisitor,
// for example, visit_int(int).
// Specify the subclass name as the Impl template parameter. Then calling
// ArgVisitor::visit for some argument will dispatch to a visit method
// specific to the argument type. For example, if the argument type is
// double then visit_double(double) method of a subclass will be called.
// If the subclass doesn't contain a method with this signature, then
// a corresponding method of ArgVisitor will be called.
//
// Example:
// class MyArgVisitor : public ArgVisitor<MyArgVisitor, void> {
// public:
// void visit_int(int value) { print("{}", value); }
// void visit_double(double value) { print("{}", value ); }
// };
//
// ArgVisitor uses the curiously recurring template pattern:
// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
template <typename Impl, typename Result>
class ArgVisitor {
public:
Result visit_unhandled_arg() { return Result(); }
Result visit_int(int value) {
return FMT_DISPATCH(visit_any_int(value));
}
Result visit_long_long(LongLong value) {
return FMT_DISPATCH(visit_any_int(value));
}
Result visit_any_int(LongLong) {
return FMT_DISPATCH(visit_unhandled_arg());
}
Result visit_uint(unsigned value) {
return FMT_DISPATCH(visit_any_uint(value));
}
Result visit_ulong_long(ULongLong value) {
return FMT_DISPATCH(visit_any_uint(value));
}
Result visit_any_uint(ULongLong) {
return FMT_DISPATCH(visit_unhandled_arg());
}
Result visit_double(double) {
return FMT_DISPATCH(visit_unhandled_arg());
}
Result visit_long_double(long double) {
return FMT_DISPATCH(visit_unhandled_arg());
}
Result visit_char(int) {
return FMT_DISPATCH(visit_unhandled_arg());
}
Result visit_string(StringValue<char>) {
return FMT_DISPATCH(visit_unhandled_arg());
}
Result visit_wstring(StringValue<wchar_t>) {
return FMT_DISPATCH(visit_unhandled_arg());
}
Result visit_pointer(const void *) {
return FMT_DISPATCH(visit_unhandled_arg());
}
Result visit_custom(Arg::CustomValue) {
return FMT_DISPATCH(visit_unhandled_arg());
}
Result visit(const Arg &arg) {
switch (arg.type) {
default:
assert(false);
// Fall through.
case Arg::INT:
return FMT_DISPATCH(visit_int(arg.int_value));
case Arg::UINT:
return FMT_DISPATCH(visit_uint(arg.uint_value));
case Arg::LONG_LONG:
return FMT_DISPATCH(visit_long_long(arg.long_long_value));
case Arg::ULONG_LONG:
return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value));
case Arg::DOUBLE:
return FMT_DISPATCH(visit_double(arg.double_value));
case Arg::LONG_DOUBLE:
return FMT_DISPATCH(visit_long_double(arg.long_double_value));
case Arg::CHAR:
return FMT_DISPATCH(visit_char(arg.int_value));
case Arg::STRING:
return FMT_DISPATCH(visit_string(arg.string));
case Arg::WSTRING:
return FMT_DISPATCH(visit_wstring(arg.wstring));
case Arg::POINTER:
return FMT_DISPATCH(visit_pointer(arg.pointer_value));
case Arg::CUSTOM:
return FMT_DISPATCH(visit_custom(arg.custom));
}
}
};
class RuntimeError : public std::runtime_error {
protected:
RuntimeError() : std::runtime_error("") {}

View File

@ -42,6 +42,7 @@
#undef max
using fmt::StringRef;
using fmt::internal::Arg;
namespace {
std::string GetSystemErrorMessage(int error_code) {
@ -75,52 +76,96 @@ TEST(UtilTest, Increment) {
EXPECT_STREQ("200", s);
}
#define EXPECT_ARG_(Char, type_code, Type, field, value) { \
Type expected_value = static_cast<Type>(value); \
fmt::internal::Arg arg = \
fmt::internal::MakeArg<Char>(expected_value); \
EXPECT_EQ(fmt::internal::Arg::type_code, arg.type); \
EXPECT_EQ(expected_value, arg.field); \
template <Arg::Type>
struct ArgInfo;
#define ARG_INFO(type_code, Type, field) \
template <> \
struct ArgInfo<Arg::type_code> { \
static Type get(const Arg &arg) { return arg.field; } \
};
ARG_INFO(INT, int, int_value);
ARG_INFO(UINT, unsigned, uint_value);
ARG_INFO(LONG_LONG, fmt::LongLong, long_long_value);
ARG_INFO(ULONG_LONG, fmt::ULongLong, ulong_long_value);
ARG_INFO(DOUBLE, double, double_value);
ARG_INFO(LONG_DOUBLE, long double, long_double_value);
ARG_INFO(CHAR, int, int_value);
ARG_INFO(STRING, const char *, string.value);
ARG_INFO(WSTRING, const wchar_t *, wstring.value);
ARG_INFO(POINTER, const void *, pointer_value);
ARG_INFO(CUSTOM, Arg::CustomValue, custom);
#define CHECK_ARG_INFO(Type, field, value) { \
Arg arg = {Arg::Type}; \
arg.field = value; \
EXPECT_EQ(value, ArgInfo<Arg::Type>::get(arg)); \
}
#define EXPECT_ARG(type_code, Type, field, value) \
EXPECT_ARG_(char, type_code, Type, field, value)
TEST(UtilTest, ArgInfo) {
CHECK_ARG_INFO(INT, int_value, 42);
CHECK_ARG_INFO(UINT, uint_value, 42);
CHECK_ARG_INFO(LONG_LONG, long_long_value, 42);
CHECK_ARG_INFO(ULONG_LONG, ulong_long_value, 42);
CHECK_ARG_INFO(DOUBLE, double_value, 4.2);
CHECK_ARG_INFO(LONG_DOUBLE, long_double_value, 4.2);
CHECK_ARG_INFO(CHAR, int_value, 'x');
CHECK_ARG_INFO(STRING, string.value, "abc");
CHECK_ARG_INFO(WSTRING, wstring.value, L"abc");
int p = 0;
CHECK_ARG_INFO(POINTER, pointer_value, &p);
Arg arg = {Arg::CUSTOM};
arg.custom.value = &p;
EXPECT_EQ(&p, ArgInfo<Arg::CUSTOM>::get(arg).value);
}
#define EXPECT_ARG_(Char, type_code, Type, value) { \
Type expected_value = static_cast<Type>(value); \
Arg arg = fmt::internal::MakeArg<Char>(expected_value); \
EXPECT_EQ(Arg::type_code, arg.type); \
EXPECT_EQ(expected_value, ArgInfo<Arg::type_code>::get(arg)); \
}
#define EXPECT_ARG(type_code, Type, value) \
EXPECT_ARG_(char, type_code, Type, value)
#define EXPECT_ARGW(type_code, Type, value) \
EXPECT_ARG_(wchar_t, type_code, Type, value)
#define EXPECT_ARGW(type_code, Type, field, value) \
EXPECT_ARG_(wchar_t, type_code, Type, field, value)
TEST(UtilTest, MakeArg) {
// Test bool.
EXPECT_ARG(INT, bool, int_value, true);
EXPECT_ARG(INT, bool, true);
// Test char.
EXPECT_ARG(CHAR, signed char, int_value, 'a');
EXPECT_ARG(CHAR, signed char, int_value, SCHAR_MIN);
EXPECT_ARG(CHAR, signed char, int_value, SCHAR_MAX);
EXPECT_ARG(CHAR, unsigned char, int_value, 'a');
EXPECT_ARG(CHAR, unsigned char, int_value, UCHAR_MAX );
EXPECT_ARG(CHAR, char, int_value, 'a');
EXPECT_ARG(CHAR, char, int_value, CHAR_MIN);
EXPECT_ARG(CHAR, char, int_value, CHAR_MAX);
EXPECT_ARG(CHAR, signed char, 'a');
EXPECT_ARG(CHAR, signed char, SCHAR_MIN);
EXPECT_ARG(CHAR, signed char, SCHAR_MAX);
EXPECT_ARG(CHAR, unsigned char, 'a');
EXPECT_ARG(CHAR, unsigned char, UCHAR_MAX );
EXPECT_ARG(CHAR, char, 'a');
EXPECT_ARG(CHAR, char, CHAR_MIN);
EXPECT_ARG(CHAR, char, CHAR_MAX);
// Test wchar_t.
EXPECT_ARGW(CHAR, wchar_t, int_value, L'a');
EXPECT_ARGW(CHAR, wchar_t, int_value, WCHAR_MIN);
EXPECT_ARGW(CHAR, wchar_t, int_value, WCHAR_MAX);
EXPECT_ARGW(CHAR, wchar_t, L'a');
EXPECT_ARGW(CHAR, wchar_t, WCHAR_MIN);
EXPECT_ARGW(CHAR, wchar_t, WCHAR_MAX);
// Test short.
EXPECT_ARG(INT, short, int_value, 42);
EXPECT_ARG(INT, short, int_value, SHRT_MIN);
EXPECT_ARG(INT, short, int_value, SHRT_MAX);
EXPECT_ARG(UINT, unsigned short, uint_value, 42);
EXPECT_ARG(UINT, unsigned short, uint_value, USHRT_MAX);
EXPECT_ARG(INT, short, 42);
EXPECT_ARG(INT, short, SHRT_MIN);
EXPECT_ARG(INT, short, SHRT_MAX);
EXPECT_ARG(UINT, unsigned short, 42);
EXPECT_ARG(UINT, unsigned short, USHRT_MAX);
// Test int.
EXPECT_ARG(INT, int, int_value, 42);
EXPECT_ARG(INT, int, int_value, INT_MIN);
EXPECT_ARG(INT, int, int_value, INT_MAX);
EXPECT_ARG(UINT, unsigned, uint_value, 42);
EXPECT_ARG(UINT, unsigned, uint_value, UINT_MAX);
EXPECT_ARG(INT, int, 42);
EXPECT_ARG(INT, int, INT_MIN);
EXPECT_ARG(INT, int, INT_MAX);
EXPECT_ARG(UINT, unsigned, 42);
EXPECT_ARG(UINT, unsigned, UINT_MAX);
// Test long.
#if LONG_MAX == INT_MAX
@ -134,62 +179,96 @@ TEST(UtilTest, MakeArg) {
# define long_value long_long_value
# define ulong_value ulong_long_value
#endif
EXPECT_ARG(LONG, long, long_value, 42);
EXPECT_ARG(LONG, long, long_value, LONG_MIN);
EXPECT_ARG(LONG, long, long_value, LONG_MAX);
EXPECT_ARG(ULONG, unsigned long, ulong_value, 42);
EXPECT_ARG(ULONG, unsigned long, ulong_value, ULONG_MAX);
EXPECT_ARG(LONG, long, 42);
EXPECT_ARG(LONG, long, LONG_MIN);
EXPECT_ARG(LONG, long, LONG_MAX);
EXPECT_ARG(ULONG, unsigned long, 42);
EXPECT_ARG(ULONG, unsigned long, ULONG_MAX);
// Test long long.
EXPECT_ARG(LONG_LONG, fmt::LongLong, long_long_value, 42);
EXPECT_ARG(LONG_LONG, fmt::LongLong, long_long_value, LLONG_MIN);
EXPECT_ARG(LONG_LONG, fmt::LongLong, long_long_value, LLONG_MAX);
EXPECT_ARG(ULONG_LONG, fmt::ULongLong, ulong_long_value, 42);
EXPECT_ARG(ULONG_LONG, fmt::ULongLong, ulong_long_value, ULLONG_MAX);
EXPECT_ARG(LONG_LONG, fmt::LongLong, 42);
EXPECT_ARG(LONG_LONG, fmt::LongLong, LLONG_MIN);
EXPECT_ARG(LONG_LONG, fmt::LongLong, LLONG_MAX);
EXPECT_ARG(ULONG_LONG, fmt::ULongLong, 42);
EXPECT_ARG(ULONG_LONG, fmt::ULongLong, ULLONG_MAX);
// Test float.
EXPECT_ARG(DOUBLE, float, double_value, 4.2);
EXPECT_ARG(DOUBLE, float, double_value, FLT_MIN);
EXPECT_ARG(DOUBLE, float, double_value, FLT_MAX);
EXPECT_ARG(DOUBLE, float, 4.2);
EXPECT_ARG(DOUBLE, float, FLT_MIN);
EXPECT_ARG(DOUBLE, float, FLT_MAX);
// Test double.
EXPECT_ARG(DOUBLE, double, double_value, 4.2);
EXPECT_ARG(DOUBLE, double, double_value, DBL_MIN);
EXPECT_ARG(DOUBLE, double, double_value, DBL_MAX);
EXPECT_ARG(DOUBLE, double, 4.2);
EXPECT_ARG(DOUBLE, double, DBL_MIN);
EXPECT_ARG(DOUBLE, double, DBL_MAX);
// Test long double.
EXPECT_ARG(LONG_DOUBLE, long double, long_double_value, 4.2);
EXPECT_ARG(LONG_DOUBLE, long double, long_double_value, LDBL_MIN);
EXPECT_ARG(LONG_DOUBLE, long double, long_double_value, LDBL_MAX);
EXPECT_ARG(LONG_DOUBLE, long double, 4.2);
EXPECT_ARG(LONG_DOUBLE, long double, LDBL_MIN);
EXPECT_ARG(LONG_DOUBLE, long double, LDBL_MAX);
// Test string.
char STR[] = "test";
EXPECT_ARG(STRING, char*, string.value, STR);
EXPECT_ARG(STRING, const char*, string.value, STR);
EXPECT_ARG(STRING, std::string, string.value, STR);
EXPECT_ARG(STRING, fmt::StringRef, string.value, STR);
EXPECT_ARG(STRING, char*, STR);
EXPECT_ARG(STRING, const char*, STR);
EXPECT_ARG(STRING, std::string, STR);
EXPECT_ARG(STRING, fmt::StringRef, STR);
// Test wide string.
wchar_t WSTR[] = L"test";
EXPECT_ARGW(WSTRING, wchar_t*, wstring.value, WSTR);
EXPECT_ARGW(WSTRING, const wchar_t*, wstring.value, WSTR);
EXPECT_ARGW(WSTRING, std::wstring, wstring.value, WSTR);
EXPECT_ARGW(WSTRING, fmt::WStringRef, wstring.value, WSTR);
EXPECT_ARGW(WSTRING, wchar_t*, WSTR);
EXPECT_ARGW(WSTRING, const wchar_t*, WSTR);
EXPECT_ARGW(WSTRING, std::wstring, WSTR);
EXPECT_ARGW(WSTRING, fmt::WStringRef, WSTR);
int n = 42;
EXPECT_ARG(POINTER, void*, pointer_value, &n);
EXPECT_ARG(POINTER, const void*, pointer_value, &n);
EXPECT_ARG(POINTER, void*, &n);
EXPECT_ARG(POINTER, const void*, &n);
::Test t;
fmt::internal::Arg arg = fmt::internal::MakeArg<char>(t);
EXPECT_EQ(fmt::internal::Arg::CUSTOM, arg.type);
arg.custom.value = &t;
EXPECT_EQ(&t, arg.custom.value);
fmt::Writer w;
fmt::BasicFormatter<char> formatter(w);
arg.custom.format(&formatter, &t, "}");
EXPECT_EQ("test", w.str());
}
struct Result {
fmt::internal::Arg arg;
Result() : arg(fmt::internal::MakeArg<char>(0xdeadbeef)) {}
template <typename T>
Result(const T& value) : arg(fmt::internal::MakeArg<char>(value)) {}
};
struct TestVisitor : fmt::internal::ArgVisitor<TestVisitor, Result> {
Result visit_int(int value) { return value; }
Result visit_uint(unsigned value) { return value; }
Result visit_long_long(fmt::LongLong value) { return value; }
Result visit_ulong_long(fmt::ULongLong value) { return value; }
Result visit_double(double value) { return value; }
Result visit_long_double(long double value) { return value; }
};
#define EXPECT_RESULT(type_code, value) { \
Result result = TestVisitor().visit(MakeArg<char>(value)); \
EXPECT_EQ(Arg::type_code, result.arg.type); \
EXPECT_EQ(value, ArgInfo<Arg::type_code>::get(result.arg)); \
}
TEST(UtilTest, ArgVisitor) {
using fmt::internal::MakeArg;
EXPECT_RESULT(INT, 42);
EXPECT_RESULT(UINT, 42u);
EXPECT_RESULT(LONG_LONG, 42ll);
EXPECT_RESULT(ULONG_LONG, 42ull);
EXPECT_RESULT(DOUBLE, 4.2);
// TODO
}
// Tests fmt::internal::CountDigits for integer type Int.
template <typename Int>
void TestCountDigits(Int) {