From 418659adbe9be1646cff3870a743706327fd77c1 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 3 Mar 2018 14:04:59 -0800 Subject: [PATCH] Fix compilation errors on gcc 4.4 --- include/fmt/core.h | 20 ++- include/fmt/format.h | 302 +++++++++++++++++++--------------- include/fmt/ostream.h | 4 +- include/fmt/printf.h | 46 +++--- test/custom-formatter-test.cc | 2 +- test/format-impl-test.cc | 4 +- test/format-test.cc | 13 +- test/util-test.cc | 42 ++--- 8 files changed, 241 insertions(+), 192 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 58d0e7bb..670fce2b 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -167,6 +167,11 @@ # define FMT_USE_EXPERIMENTAL_STRING_VIEW #endif +// std::result_of is defined in in gcc 4.4. +#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 404 +# include +#endif + namespace fmt { // An implementation of declval for pre-C++11 compilers such as gcc 4. @@ -302,8 +307,6 @@ class basic_buffer { std::size_t capacity_; protected: - typedef const T &const_reference; - basic_buffer(T *p = FMT_NULL, std::size_t size = 0, std::size_t capacity = 0) FMT_NOEXCEPT: ptr_(p), size_(size), capacity_(capacity) {} @@ -322,6 +325,7 @@ class basic_buffer { public: typedef T value_type; + typedef const T &const_reference; virtual ~basic_buffer() {} @@ -655,6 +659,16 @@ enum { MAX_PACKED_ARGS = 15 }; template class arg_map; + +template +struct result_of; + +template +struct result_of { + // A workaround for gcc 4.4 that doesn't allow F to be a reference. + typedef typename std::result_of< + typename std::remove_reference::type(Args...)>::type type; +}; } // A formatting argument. It is a trivially copyable/constructible type to @@ -669,7 +683,7 @@ class basic_arg { friend FMT_CONSTEXPR basic_arg internal::make_arg(const T &value); template - friend FMT_CONSTEXPR typename std::result_of::type + friend FMT_CONSTEXPR typename internal::result_of::type visit(Visitor &&vis, basic_arg arg); friend class basic_format_args; diff --git a/include/fmt/format.h b/include/fmt/format.h index 5e771bcf..b388fdbe 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -156,6 +156,13 @@ # endif #endif +// A workaround for gcc 4.4 that doesn't support union members with ctors. +#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 404 +# define FMT_UNION struct +#else +# define FMT_UNION union +#endif + // Some compilers masquerade as both MSVC and GCC-likes or otherwise support // __builtin_clz and __builtin_clzll, so only define FMT_BUILTIN_CLZ using the // MSVC intrinsics if the clz and clzll builtins are not available. @@ -225,6 +232,13 @@ FMT_CONSTEXPR auto end(const C &c) -> decltype(c.end()) { return c.end(); } template FMT_CONSTEXPR T *end(T (&array)[N]) FMT_NOEXCEPT { return array + N; } +// For std::result_of in gcc 4.4. +template +struct function { + template + struct result { typedef Result type; }; +}; + struct dummy_int { int data[2]; operator int() const { return 0; } @@ -1016,7 +1030,7 @@ struct monostate {}; \endrst */ template -FMT_CONSTEXPR typename std::result_of::type +FMT_CONSTEXPR typename internal::result_of::type visit(Visitor &&vis, basic_arg arg) { typedef typename Context::char_type char_type; switch (arg.type_) { @@ -1051,7 +1065,7 @@ FMT_CONSTEXPR typename std::result_of::type case internal::custom_type: return vis(typename basic_arg::handle(arg.value_.custom)); } - return typename std::result_of::type(); + return typename internal::result_of::type(); } enum alignment { @@ -1405,33 +1419,35 @@ class arg_formatter_base { write(value); } + struct char_spec_handler : internal::error_handler { + arg_formatter_base &formatter; + char_type value; + + char_spec_handler(arg_formatter_base& f, char_type val) + : formatter(f), value(val) {} + + void on_int() { formatter.writer_.write_int(value, formatter.specs_); } + void on_char() { formatter.write_char(value); } + }; + void operator()(char_type value) { - struct spec_handler : internal::error_handler { - arg_formatter_base &formatter; - char_type value; - - spec_handler(arg_formatter_base& f, char_type val) - : formatter(f), value(val) {} - - void on_int() { formatter.writer_.write_int(value, formatter.specs_); } - void on_char() { formatter.write_char(value); } - }; - internal::handle_char_specs(specs_, spec_handler(*this, value)); + internal::handle_char_specs(specs_, char_spec_handler(*this, value)); } + struct cstring_spec_handler : internal::error_handler { + arg_formatter_base &formatter; + const char_type *value; + + cstring_spec_handler(arg_formatter_base &f, const char_type *val) + : formatter(f), value(val) {} + + void on_string() { formatter.write(value); } + void on_pointer() { formatter.write_pointer(value); } + }; + void operator()(const char_type *value) { - struct spec_handler : internal::error_handler { - arg_formatter_base &formatter; - const char_type *value; - - spec_handler(arg_formatter_base &f, const char_type *val) - : formatter(f), value(val) {} - - void on_string() { formatter.write(value); } - void on_pointer() { formatter.write_pointer(value); } - }; internal::handle_cstring_type_spec( - specs_.type_, spec_handler(*this, value)); + specs_.type_, cstring_spec_handler(*this, value)); } void operator()(basic_string_view value) { @@ -1480,20 +1496,20 @@ FMT_CONSTEXPR unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) { } template -class custom_formatter { +class custom_formatter: public function { private: Context &ctx_; public: explicit custom_formatter(Context &ctx): ctx_(ctx) {} - bool operator()(typename basic_arg::handle h) { + bool operator()(typename basic_arg::handle h) const { h.format(ctx_); return true; } template - bool operator()(T) { return false; } + bool operator()(T) const { return false; } }; template @@ -1505,12 +1521,13 @@ struct is_integer { }; template -class width_checker { +class width_checker: public function { public: explicit FMT_CONSTEXPR width_checker(ErrorHandler &eh) : handler_(eh) {} template - FMT_CONSTEXPR typename std::enable_if< + FMT_CONSTEXPR + typename std::enable_if< is_integer::value, unsigned long long>::type operator()(T value) { if (is_negative(value)) handler_.on_error("negative width"); @@ -1529,7 +1546,7 @@ class width_checker { }; template -class precision_checker { +class precision_checker: public function { public: explicit FMT_CONSTEXPR precision_checker(ErrorHandler &eh) : handler_(eh) {} @@ -1716,7 +1733,7 @@ struct arg_ref { } Kind kind; - union { + FMT_UNION { unsigned index; basic_string_view name; }; @@ -2103,7 +2120,8 @@ void handle_dynamic_spec( /** The default argument formatter. */ template -class arg_formatter: public internal::arg_formatter_base { +class arg_formatter: + public internal::function, public internal::arg_formatter_base { private: typedef typename Range::value_type char_type; typedef decltype(internal::declval().begin()) iterator; @@ -2129,7 +2147,7 @@ class arg_formatter: public internal::arg_formatter_base { using base::operator(); /** Formats an argument of a custom (user-defined) type. */ - void operator()(typename basic_arg::handle handle) { + void operator()(typename basic_arg::handle handle) const { handle.format(ctx_); } }; @@ -2516,10 +2534,10 @@ class basic_writer { Formats *value* and writes it to the buffer. \endrst */ - template + template typename std::enable_if::value, void>::type - write(T value, FormatSpecs... specs) { - format_specs s(specs...); + write(T value, FormatSpec spec, FormatSpecs... specs) { + format_specs s(spec, specs...); s.align_ = ALIGN_RIGHT; write_int(value, s); } @@ -2619,48 +2637,50 @@ void basic_writer::write_str( write_str(data, size, spec); } +template +struct float_spec_handler { + Char type; + bool upper; + + explicit float_spec_handler(Char t) : type(t), upper(false) {} + + void on_general() { + if (type == 'G') + 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 + // MSVC's printf doesn't support 'F'. + type = 'f'; +#endif + } + } + + void on_hex() { + if (type == 'A') + upper = true; + } + + void on_error() { + FMT_THROW(format_error("invalid type specifier")); + } +}; + template template void basic_writer::write_double(T value, const format_specs &spec) { // Check type. - struct spec_handler { - char_type type; - bool upper; - - explicit spec_handler(char_type t) : type(t), upper(false) {} - - void on_general() { - if (type == 'G') - 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 - // MSVC's printf doesn't support 'F'. - type = 'f'; -#endif - } - } - - void on_hex() { - if (type == 'A') - upper = true; - } - - void on_error() { - FMT_THROW(format_error("invalid type specifier")); - } - }; - spec_handler handler(spec.type()); + float_spec_handler handler(spec.type()); internal::handle_float_type_spec(spec.type(), handler); char sign = 0; @@ -3049,7 +3069,17 @@ struct formatter -struct dynamic_formatter { +class dynamic_formatter { + private: + struct null_handler: internal::error_handler { + void on_align(alignment) {} + void on_plus() {} + void on_minus() {} + void on_space() {} + void on_hash() {} + }; + + public: template auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { auto it = internal::null_terminating_iterator(ctx); @@ -3062,13 +3092,6 @@ struct dynamic_formatter { template auto format(const T &val, FormatContext &ctx) -> decltype(ctx.begin()) { handle_specs(ctx); - struct null_handler : internal::error_handler { - void on_align(alignment) {} - void on_plus() {} - void on_minus() {} - void on_space() {} - void on_hash() {} - }; internal::specs_checker checker(null_handler(), internal::get_type::value); checker.on_align(specs_.align()); @@ -3115,66 +3138,70 @@ typename basic_context::format_arg return arg; } +template +struct format_handler : internal::error_handler { + typedef internal::null_terminating_iterator iterator; + typedef typename ArgFormatter::range range; + + format_handler(range r, basic_string_view str, + basic_format_args format_args) + : context(r.begin(), str, format_args) {} + + void on_text(iterator begin, iterator end) { + size_t size = end - begin; + auto out = context.begin(); + auto &&it = internal::reserve(out, size); + it = std::copy_n(begin, size, it); + context.advance_to(out); + } + + void on_arg_id() { arg = context.next_arg(); } + void on_arg_id(unsigned id) { + context.parse_context().check_arg_id(id); + arg = context.get_arg(id); + } + void on_arg_id(basic_string_view id) { + arg = context.get_arg(id); + } + + void on_replacement_field(iterator it) { + context.parse_context().advance_to(pointer_from(it)); + using internal::custom_formatter; + if (visit(custom_formatter(context), arg)) + return; + basic_format_specs specs; + visit(ArgFormatter(context, specs), arg); + } + + iterator on_format_specs(iterator it) { + auto& parse_ctx = context.parse_context(); + parse_ctx.advance_to(pointer_from(it)); + using internal::custom_formatter; + if (visit(custom_formatter(context), arg)) + return iterator(parse_ctx); + basic_format_specs specs; + using internal::specs_handler; + internal::specs_checker> + handler(specs_handler(specs, context), arg.type()); + it = parse_format_specs(it, handler); + if (*it != '}') + on_error("missing '}' in format string"); + parse_ctx.advance_to(pointer_from(it)); + visit(ArgFormatter(context, specs), arg); + return it; + } + + Context context; + basic_arg arg; +}; + /** Formats arguments and writes the output to the buffer. */ template typename Context::iterator do_vformat_to(typename ArgFormatter::range out, basic_string_view format_str, basic_format_args args) { typedef internal::null_terminating_iterator iterator; - typedef typename ArgFormatter::range range; - - struct handler : internal::error_handler { - handler(range r, basic_string_view str, - basic_format_args format_args) - : context(r.begin(), str, format_args) {} - - void on_text(iterator begin, iterator end) { - size_t size = end - begin; - auto out = context.begin(); - auto &&it = internal::reserve(out, size); - it = std::copy_n(begin, size, it); - context.advance_to(out); - } - - void on_arg_id() { arg = context.next_arg(); } - void on_arg_id(unsigned id) { - context.parse_context().check_arg_id(id); - arg = context.get_arg(id); - } - void on_arg_id(basic_string_view id) { - arg = context.get_arg(id); - } - - void on_replacement_field(iterator it) { - context.parse_context().advance_to(pointer_from(it)); - using internal::custom_formatter; - if (visit(custom_formatter(context), arg)) - return; - basic_format_specs specs; - visit(ArgFormatter(context, specs), arg); - } - - iterator on_format_specs(iterator it) { - auto& parse_ctx = context.parse_context(); - parse_ctx.advance_to(pointer_from(it)); - using internal::custom_formatter; - if (visit(custom_formatter(context), arg)) - return iterator(parse_ctx); - basic_format_specs specs; - using internal::specs_handler; - internal::specs_checker> - handler(specs_handler(specs, context), arg.type()); - it = parse_format_specs(it, handler); - if (*it != '}') - on_error("missing '}' in format string"); - parse_ctx.advance_to(pointer_from(it)); - visit(ArgFormatter(context, specs), arg); - return it; - } - - Context context; - basic_arg arg; - } h(out, format_str, args); + format_handler h(out, format_str, args); parse_format_string(iterator(format_str.begin(), format_str.end()), h); return h.context.begin(); } @@ -3250,7 +3277,8 @@ arg_join join(It begin, It end, wstring_view sep) { return arg_join(begin, end, sep); } -#if FMT_USE_TRAILING_RETURN +// The following causes ICE in gcc 4.4. +#if FMT_USE_TRAILING_RETURN && (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 405) template auto join(const Range &range, string_view sep) -> arg_join { diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index e28170d4..3ec8919a 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -61,8 +61,8 @@ class convert_to_int { private: template static decltype( - std::declval&>() << std::declval(), std::true_type()) - test(int); + internal::declval&>() + << internal::declval(), std::true_type()) test(int); template static std::false_type test(...); diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 9720d972..4bc92253 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -19,7 +19,7 @@ namespace internal { // Checks if a value fits in int - used to avoid warnings about comparing // signed and unsigned integers. template -struct IntChecker { +struct int_checker { template static bool fits_in_int(T value) { unsigned max = std::numeric_limits::max(); @@ -29,7 +29,7 @@ struct IntChecker { }; template <> -struct IntChecker { +struct int_checker { template static bool fits_in_int(T value) { return value >= std::numeric_limits::min() && @@ -38,12 +38,12 @@ struct IntChecker { static bool fits_in_int(int) { return true; } }; -class PrintfPrecisionHandler { +class printf_precision_handler: public function { public: template typename std::enable_if::value, int>::type operator()(T value) { - if (!IntChecker::is_signed>::fits_in_int(value)) + if (!int_checker::is_signed>::fits_in_int(value)) FMT_THROW(format_error("number is too big")); return static_cast(value); } @@ -57,7 +57,7 @@ class PrintfPrecisionHandler { }; // An argument visitor that returns true iff arg is a zero integer. -class IsZeroInt { +class is_zero_int: public function { public: template typename std::enable_if::value, bool>::type @@ -77,7 +77,7 @@ struct make_unsigned_or_bool { }; template -class ArgConverter { +class arg_converter: public function { private: typedef typename Context::char_type Char; @@ -85,7 +85,7 @@ class ArgConverter { typename Context::char_type type_; public: - ArgConverter(basic_arg &arg, Char type) + arg_converter(basic_arg &arg, Char type) : arg_(arg), type_(type) {} void operator()(bool value) { @@ -134,19 +134,19 @@ class ArgConverter { // unsigned). template void convert_arg(basic_arg &arg, Char type) { - visit(ArgConverter(arg, type), arg); + visit(arg_converter(arg, type), arg); } // Converts an integer argument to char for printf. template -class CharConverter { +class char_converter: public function { private: basic_arg &arg_; - FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); + FMT_DISALLOW_COPY_AND_ASSIGN(char_converter); public: - explicit CharConverter(basic_arg &arg) : arg_(arg) {} + explicit char_converter(basic_arg &arg) : arg_(arg) {} template typename std::enable_if::value>::type @@ -163,16 +163,16 @@ class CharConverter { // Checks if an argument is a valid printf width specifier and sets // left alignment if it is negative. template -class PrintfWidthHandler { +class printf_width_handler: public function { private: typedef basic_format_specs format_specs; format_specs &spec_; - FMT_DISALLOW_COPY_AND_ASSIGN(PrintfWidthHandler); + FMT_DISALLOW_COPY_AND_ASSIGN(printf_width_handler); public: - explicit PrintfWidthHandler(format_specs &spec) : spec_(spec) {} + explicit printf_width_handler(format_specs &spec) : spec_(spec) {} template typename std::enable_if::value, unsigned>::type @@ -213,10 +213,11 @@ class basic_printf_context; \endrst */ template -class printf_arg_formatter : public internal::arg_formatter_base { +class printf_arg_formatter: + public internal::function, public internal::arg_formatter_base { private: typedef typename Range::value_type char_type; - typedef decltype(std::declval().begin()) iterator; + typedef decltype(internal::declval().begin()) iterator; typedef internal::arg_formatter_base base; typedef basic_printf_context context_type; @@ -417,7 +418,7 @@ unsigned basic_printf_context::parse_header( } else if (*it == '*') { ++it; spec.width_ = - visit(internal::PrintfWidthHandler(spec), get_arg(it)); + visit(internal::printf_width_handler(spec), get_arg(it)); } return arg_index; } @@ -453,14 +454,14 @@ void basic_printf_context::format() { } else if (*it == '*') { ++it; spec.precision_ = - visit(internal::PrintfPrecisionHandler(), get_arg(it)); + visit(internal::printf_precision_handler(), get_arg(it)); } else { spec.precision_ = 0; } } format_arg arg = get_arg(it, arg_index); - if (spec.flag(HASH_FLAG) && visit(internal::IsZeroInt(), arg)) + if (spec.flag(HASH_FLAG) && visit(internal::is_zero_int(), arg)) spec.flags_ &= ~internal::to_unsigned(HASH_FLAG); if (spec.fill_ == '0') { if (arg.is_arithmetic()) @@ -514,7 +515,7 @@ void basic_printf_context::format() { break; case 'c': // TODO: handle wchar_t - visit(internal::CharConverter(arg), arg); + visit(internal::char_converter(arg), arg); break; } } @@ -539,8 +540,7 @@ struct printf_context { std::back_insert_iterator, typename Buffer::value_type> type; }; -typedef basic_format_args< - typename printf_context::type> printf_args; +typedef basic_format_args::type> printf_args; inline std::string vsprintf(string_view format, printf_args args) { memory_buffer buffer; @@ -565,7 +565,7 @@ inline std::string sprintf(string_view format_str, const Args & ... args) { inline std::wstring vsprintf( wstring_view format, - basic_format_args::type> args) { + basic_format_args::type> args) { wmemory_buffer buffer; printf(buffer, format, args); return to_string(buffer); diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index d8be5375..59be29ca 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -18,7 +18,7 @@ class CustomArgFormatter : public fmt::arg_formatter> { public: typedef fmt::back_insert_range range; - typedef decltype(std::declval().begin()) iterator; + typedef decltype(fmt::internal::declval().begin()) iterator; typedef fmt::arg_formatter base; CustomArgFormatter(fmt::basic_context &ctx, diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index fea4d7dd..3d7f1150 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -44,7 +44,7 @@ #undef max template -struct ValueExtractor { +struct ValueExtractor: fmt::internal::function { T operator()(T value) { return value; } @@ -59,7 +59,7 @@ struct ValueExtractor { TEST(FormatTest, ArgConverter) { long long value = std::numeric_limits::max(); auto arg = fmt::internal::make_arg(value); - visit(fmt::internal::ArgConverter(arg, 'd'), arg); + visit(fmt::internal::arg_converter(arg, 'd'), arg); EXPECT_EQ(value, visit(ValueExtractor(), arg)); } diff --git a/test/format-test.cc b/test/format-test.cc index 7c39ac04..801beea4 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1205,7 +1205,9 @@ TEST(FormatterTest, FormatPointer) { EXPECT_EQ("0x" + std::string(sizeof(void*) * CHAR_BIT / 4, 'f'), format("{0}", reinterpret_cast(~uintptr_t()))); EXPECT_EQ("0x1234", format("{}", fmt::ptr(reinterpret_cast(0x1234)))); - EXPECT_EQ("0x0", format("{}", nullptr)); +#if FMT_USE_NULLPTR + EXPECT_EQ("0x0", format("{}", FMT_NULL)); +#endif } TEST(FormatterTest, FormatString) { @@ -1450,7 +1452,7 @@ TEST(FormatTest, JoinArg) { EXPECT_EQ(L"(1, 2, 3)", format(L"({})", join(v1, v1 + 3, L", "))); EXPECT_EQ("1, 2, 3", format("{0:{1}}", join(v1, v1 + 3, ", "), 1)); -#if FMT_HAS_GXX_CXX11 +#if FMT_USE_TRAILING_RETURN && (!FMT_GCC_VERSION || FMT_GCC_VERSION >= 405) EXPECT_EQ("(1, 2, 3)", format("({})", join(v1, ", "))); EXPECT_EQ("(+01.20, +03.40)", format("({:+06.2f})", join(v2, ", "))); #endif @@ -1551,7 +1553,8 @@ TEST(FormatTest, FixedEnum) { typedef fmt::back_insert_range buffer_range; -class mock_arg_formatter : +class mock_arg_formatter: + public fmt::internal::function, public fmt::internal::arg_formatter_base { private: MOCK_METHOD1(call, void (int value)); @@ -1602,8 +1605,8 @@ template <> struct formatter : dynamic_formatter<> { auto format(variant value, context& ctx) -> decltype(ctx.begin()) { if (value.type == variant::INT) - return dynamic_formatter::format(42, ctx); - return dynamic_formatter::format("foo", ctx); + return dynamic_formatter<>::format(42, ctx); + return dynamic_formatter<>::format("foo", ctx); } }; } diff --git a/test/util-test.cc b/test/util-test.cc index 6a16096b..eb1c09d9 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -478,12 +478,12 @@ bool operator==(custom_value lhs, custom_value rhs) { } } -template -struct MockVisitor { - // Use a unique result type to make sure that there are no undesirable - // conversions. - struct Result {}; +// Use a unique result type to make sure that there are no undesirable +// conversions. +struct Result {}; +template +struct MockVisitor: fmt::internal::function { MockVisitor() { ON_CALL(*this, visit(_)).WillByDefault(Return(Result())); } @@ -529,8 +529,9 @@ VISIT_TYPE(float, double); fmt::visit(visitor, make_arg>(value)); \ } -#define CHECK_ARG(value) { \ - typename VisitType::Type expected = value; \ +#define CHECK_ARG(value, typename_) { \ + typedef decltype(value) value_type; \ + typename_ VisitType::Type expected = value; \ CHECK_ARG_(char, expected, value) \ CHECK_ARG_(wchar_t, expected, value) \ } @@ -556,9 +557,9 @@ typename std::enable_if::value, T>::type } TYPED_TEST(NumericArgTest, MakeAndVisit) { - CHECK_ARG(test_value()); - CHECK_ARG(std::numeric_limits::min()); - CHECK_ARG(std::numeric_limits::max()); + CHECK_ARG(test_value(), typename); + CHECK_ARG(std::numeric_limits::min(), typename); + CHECK_ARG(std::numeric_limits::max(), typename); } TEST(UtilTest, CharArg) { @@ -594,22 +595,25 @@ TEST(UtilTest, PointerArg) { const void *cp = 0; CHECK_ARG_(char, cp, p); CHECK_ARG_(wchar_t, cp, p); - CHECK_ARG(cp); + CHECK_ARG(cp, ); } -TEST(UtilTest, CustomArg) { - ::Test test; - typedef typename fmt::basic_arg::handle handle; - typedef MockVisitor visitor; - testing::StrictMock v; - EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke([&](handle h) { +struct check_custom { + Result operator()(fmt::basic_arg::handle h) const { fmt::memory_buffer buffer; fmt::internal::basic_buffer &base = buffer; fmt::context ctx(std::back_inserter(base), "", fmt::format_args()); h.format(ctx); EXPECT_EQ("test", std::string(buffer.data(), buffer.size())); - return visitor::Result(); - })); + return Result(); + } +}; + +TEST(UtilTest, CustomArg) { + ::Test test; + typedef MockVisitor::handle> visitor; + testing::StrictMock v; + EXPECT_CALL(v, visit(_)).WillOnce(testing::Invoke(check_custom())); fmt::visit(v, make_arg(test)); }