From 591ad0a64a7323f4b94938d0ec9b3934b971eebf Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 14 Jul 2014 06:55:29 -0700 Subject: [PATCH] Add ArgVisitor. --- format.cc | 50 +++++------- format.h | 100 +++++++++++++++++++++++ test/util-test.cc | 203 ++++++++++++++++++++++++++++++++-------------- 3 files changed, 263 insertions(+), 90 deletions(-) diff --git a/format.cc b/format.cc index 3226b17d..e71207ba 100644 --- a/format.cc +++ b/format.cc @@ -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 { + 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::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(width); else if (!error) diff --git a/format.h b/format.h index ddc9f28d..1c5220ed 100644 --- a/format.h +++ b/format.h @@ -717,6 +717,106 @@ public: } }; +#define FMT_DISPATCH(call) static_cast(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 { +// 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 +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) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + Result visit_wstring(StringValue) { + 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("") {} diff --git a/test/util-test.cc b/test/util-test.cc index 24f464a6..fbd2492f 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -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(value); \ - fmt::internal::Arg arg = \ - fmt::internal::MakeArg(expected_value); \ - EXPECT_EQ(fmt::internal::Arg::type_code, arg.type); \ - EXPECT_EQ(expected_value, arg.field); \ +template +struct ArgInfo; + +#define ARG_INFO(type_code, Type, field) \ + template <> \ + struct ArgInfo { \ + 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::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::get(arg).value); +} + +#define EXPECT_ARG_(Char, type_code, Type, value) { \ + Type expected_value = static_cast(value); \ + Arg arg = fmt::internal::MakeArg(expected_value); \ + EXPECT_EQ(Arg::type_code, arg.type); \ + EXPECT_EQ(expected_value, ArgInfo::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(t); EXPECT_EQ(fmt::internal::Arg::CUSTOM, arg.type); - arg.custom.value = &t; + EXPECT_EQ(&t, arg.custom.value); fmt::Writer w; fmt::BasicFormatter formatter(w); arg.custom.format(&formatter, &t, "}"); EXPECT_EQ("test", w.str()); } +struct Result { + fmt::internal::Arg arg; + + Result() : arg(fmt::internal::MakeArg(0xdeadbeef)) {} + + template + Result(const T& value) : arg(fmt::internal::MakeArg(value)) {} +}; + +struct TestVisitor : fmt::internal::ArgVisitor { + 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(value)); \ + EXPECT_EQ(Arg::type_code, result.arg.type); \ + EXPECT_EQ(value, ArgInfo::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 void TestCountDigits(Int) {