From 0cce094c89bc2fef430d57a122bbab80a2ab326e Mon Sep 17 00:00:00 2001 From: Jean-Charles Lefebvre Date: Thu, 29 Dec 2016 17:24:08 +0100 Subject: [PATCH] A custom FormatSpec type can be passed as a template argument to the ArgFormatter chain (#439) --- doc/api.rst | 8 +++-- fmt/format.h | 55 ++++++++++++++++++++--------------- fmt/printf.h | 21 ++++++------- test/custom-formatter-test.cc | 16 ++++++---- test/format-test.cc | 9 ++++-- test/ostream-test.cc | 6 ++-- 6 files changed, 67 insertions(+), 48 deletions(-) diff --git a/doc/api.rst b/doc/api.rst index 5a93b4aa..b0069f0c 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -120,17 +120,19 @@ custom argument formatter class:: // A custom argument formatter that formats negative integers as unsigned // with the ``x`` format specifier. class CustomArgFormatter : - public fmt::BasicArgFormatter { + public fmt::BasicArgFormatter { public: CustomArgFormatter(fmt::BasicFormatter &f, fmt::FormatSpec &s, const char *fmt) - : fmt::BasicArgFormatter(f, s, fmt) {} + : fmt::BasicArgFormatter(f, s, fmt) {} void visit_int(int value) { if (spec().type() == 'x') visit_uint(value); // convert to unsigned and format else - fmt::BasicArgFormatter::visit_int(value); + fmt::BasicArgFormatter::visit_int(value); } }; diff --git a/fmt/format.h b/fmt/format.h index b8713978..eac2e4ed 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -431,7 +431,7 @@ typedef BasicWriter WWriter; template class ArgFormatter; -template +template class BasicPrintfArgFormatter; template +template class ArgFormatterBase : public ArgVisitor { private: BasicWriter &writer_; - FormatSpec &spec_; + Spec &spec_; FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase); @@ -1938,7 +1941,7 @@ class ArgFormatterBase : public ArgVisitor { protected: BasicWriter &writer() { return writer_; } - FormatSpec &spec() { return spec_; } + Spec &spec() { return spec_; } void write(bool value) { const char *str_value = value ? "true" : "false"; @@ -1952,7 +1955,9 @@ class ArgFormatterBase : public ArgVisitor { } public: - ArgFormatterBase(BasicWriter &w, FormatSpec &s) + typedef typename Spec SpecType; + + ArgFormatterBase(BasicWriter &w, Spec &s) : writer_(w), spec_(s) {} template @@ -2086,8 +2091,8 @@ class FormatterBase { will be called. \endrst */ -template -class BasicArgFormatter : public internal::ArgFormatterBase { +template +class BasicArgFormatter : public internal::ArgFormatterBase { private: BasicFormatter &formatter_; const Char *format_; @@ -2102,8 +2107,8 @@ class BasicArgFormatter : public internal::ArgFormatterBase { \endrst */ BasicArgFormatter(BasicFormatter &formatter, - FormatSpec &spec, const Char *fmt) - : internal::ArgFormatterBase(formatter.writer(), spec), + Spec &spec, const Char *fmt) + : internal::ArgFormatterBase(formatter.writer(), spec), formatter_(formatter), format_(fmt) {} /** Formats an argument of a custom (user-defined) type. */ @@ -2114,12 +2119,14 @@ class BasicArgFormatter : public internal::ArgFormatterBase { /** The default argument formatter. */ template -class ArgFormatter : public BasicArgFormatter, Char> { +class ArgFormatter : + public BasicArgFormatter, Char, FormatSpec> { public: /** Constructs an argument formatter object. */ ArgFormatter(BasicFormatter &formatter, FormatSpec &spec, const Char *fmt) - : BasicArgFormatter, Char>(formatter, spec, fmt) {} + : BasicArgFormatter, + Char, FormatSpec>(formatter, spec, fmt) {} }; /** This template formats data and writes the output to a writer. */ @@ -2501,16 +2508,16 @@ class BasicWriter { void write_int(T value, Spec spec); // Formats a floating-point number (double or long double). - template - void write_double(T value, const FormatSpec &spec); + template + void write_double(T value, const Spec &spec); // Writes a formatted string. template CharPtr write_str(const StrChar *s, std::size_t size, const AlignSpec &spec); - template + template void write_str(const internal::Arg::StringValue &str, - const FormatSpec &spec); + const Spec &spec); // This following methods are private to disallow writing wide characters // and strings to a char stream. If you want to print a wide string as a @@ -2529,10 +2536,10 @@ class BasicWriter { template void append_float_length(Char *&, T) {} - template + template friend class internal::ArgFormatterBase; - template + template friend class BasicPrintfArgFormatter; protected: @@ -2729,9 +2736,9 @@ typename BasicWriter::CharPtr BasicWriter::write_str( } template -template +template void BasicWriter::write_str( - const internal::Arg::StringValue &s, const FormatSpec &spec) { + const internal::Arg::StringValue &s, const Spec &spec) { // Check if StrChar is convertible to Char. internal::CharTraits::convert(StrChar()); if (spec.type_ && spec.type_ != 's') @@ -2855,7 +2862,7 @@ void BasicWriter::write_int(T value, Spec spec) { UnsignedType n = abs_value; if (spec.flag(HASH_FLAG)) { prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); + prefix[prefix_size++] = spec.type_prefix(); } unsigned num_digits = 0; do { @@ -2875,7 +2882,7 @@ void BasicWriter::write_int(T value, Spec spec) { UnsignedType n = abs_value; if (spec.flag(HASH_FLAG)) { prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); + prefix[prefix_size++] = spec.type_prefix(); } unsigned num_digits = 0; do { @@ -2923,8 +2930,8 @@ void BasicWriter::write_int(T value, Spec spec) { } template -template -void BasicWriter::write_double(T value, const FormatSpec &spec) { +template +void BasicWriter::write_double(T value, const Spec &spec) { // Check type. char type = spec.type(); bool upper = false; @@ -3661,7 +3668,7 @@ const Char *BasicFormatter::format( const Char *&format_str, const internal::Arg &arg) { using internal::Arg; const Char *s = format_str; - FormatSpec spec; + typename ArgFormatter::SpecType spec; if (*s == ':') { if (arg.type == Arg::CUSTOM) { arg.custom.format(this, arg.custom.value, &s); diff --git a/fmt/printf.h b/fmt/printf.h index 80d22441..28f32f8e 100644 --- a/fmt/printf.h +++ b/fmt/printf.h @@ -189,15 +189,16 @@ class WidthHandler : public ArgVisitor { superclass will be called. \endrst */ -template -class BasicPrintfArgFormatter : public internal::ArgFormatterBase { +template +class BasicPrintfArgFormatter : + public internal::ArgFormatterBase { private: void write_null_pointer() { this->spec().type_ = 0; this->write("(nil)"); } - typedef internal::ArgFormatterBase Base; + typedef internal::ArgFormatterBase Base; public: /** @@ -207,12 +208,12 @@ class BasicPrintfArgFormatter : public internal::ArgFormatterBase { specifier information for standard argument types. \endrst */ - BasicPrintfArgFormatter(BasicWriter &w, FormatSpec &s) - : internal::ArgFormatterBase(w, s) {} + BasicPrintfArgFormatter(BasicWriter &w, Spec &s) + : internal::ArgFormatterBase(w, s) {} /** Formats an argument of type ``bool``. */ void visit_bool(bool value) { - FormatSpec &fmt_spec = this->spec(); + Spec &fmt_spec = this->spec(); if (fmt_spec.type_ != 's') return this->visit_any_int(value); fmt_spec.type_ = 0; @@ -221,7 +222,7 @@ class BasicPrintfArgFormatter : public internal::ArgFormatterBase { /** Formats a character. */ void visit_char(int value) { - const FormatSpec &fmt_spec = this->spec(); + const Spec &fmt_spec = this->spec(); BasicWriter &w = this->writer(); if (fmt_spec.type_ && fmt_spec.type_ != 'c') w.write_int(value, fmt_spec); @@ -271,12 +272,12 @@ class BasicPrintfArgFormatter : public internal::ArgFormatterBase { /** The default printf argument formatter. */ template -class PrintfArgFormatter - : public BasicPrintfArgFormatter, Char> { +class PrintfArgFormatter : + public BasicPrintfArgFormatter, Char, FormatSpec> { public: /** Constructs an argument formatter object. */ PrintfArgFormatter(BasicWriter &w, FormatSpec &s) - : BasicPrintfArgFormatter, Char>(w, s) {} + : BasicPrintfArgFormatter, Char, FormatSpec>(w, s) {} }; /** This template formats data and writes the output to a writer. */ diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index cc9c4485..31edc90a 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -14,26 +14,30 @@ using fmt::BasicPrintfArgFormatter; // A custom argument formatter that doesn't print `-` for floating-point values // rounded to 0. -class CustomArgFormatter - : public fmt::BasicArgFormatter { +class CustomArgFormatter : + public fmt::BasicArgFormatter { public: CustomArgFormatter(fmt::BasicFormatter &f, fmt::FormatSpec &s, const char *fmt) - : fmt::BasicArgFormatter(f, s, fmt) {} + : fmt::BasicArgFormatter(f, s, fmt) {} void visit_double(double value) { if (round(value * pow(10, spec().precision())) == 0) value = 0; - fmt::BasicArgFormatter::visit_double(value); + fmt::BasicArgFormatter::visit_double(value); } }; // A custom argument formatter that doesn't print `-` for floating-point values // rounded to 0. class CustomPrintfArgFormatter : - public BasicPrintfArgFormatter { + public BasicPrintfArgFormatter { public: - typedef BasicPrintfArgFormatter Base; + typedef BasicPrintfArgFormatter Base; CustomPrintfArgFormatter(fmt::BasicWriter &w, fmt::FormatSpec &spec) : Base(w, spec) {} diff --git a/test/format-test.cc b/test/format-test.cc index c812b067..2d23bde6 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1630,13 +1630,16 @@ TEST(FormatTest, Enum) { } class MockArgFormatter : - public fmt::internal::ArgFormatterBase { + public fmt::internal::ArgFormatterBase { public: - typedef fmt::internal::ArgFormatterBase Base; + typedef fmt::internal::ArgFormatterBase Base; MockArgFormatter(fmt::BasicFormatter &f, fmt::FormatSpec &s, const char *) - : fmt::internal::ArgFormatterBase(f.writer(), s) { + : fmt::internal::ArgFormatterBase(f.writer(), s) { EXPECT_CALL(*this, visit_int(42)); } diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 4081b43f..e6b781af 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -58,10 +58,12 @@ TEST(OStreamTest, Enum) { EXPECT_EQ("0", fmt::format("{}", A)); } -struct TestArgFormatter : fmt::BasicArgFormatter { +struct TestArgFormatter : + fmt::BasicArgFormatter { TestArgFormatter(fmt::BasicFormatter &f, fmt::FormatSpec &s, const char *fmt) - : fmt::BasicArgFormatter(f, s, fmt) {} + : fmt::BasicArgFormatter(f, s, fmt) {} }; TEST(OStreamTest, CustomArg) {