From d9db89814f5a1de13898da1a250ecf09f3ca7e74 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 28 Apr 2014 08:59:29 -0700 Subject: [PATCH] Refactor action classes, Action -> Sink, add comments. --- format-test.cc | 55 ++++++---- format.cc | 9 +- format.h | 280 ++++++++++++++++++++++++++++--------------------- 3 files changed, 200 insertions(+), 144 deletions(-) diff --git a/format-test.cc b/format-test.cc index 542da0f1..fd142296 100644 --- a/format-test.cc +++ b/format-test.cc @@ -1479,32 +1479,43 @@ TEST(StringRefTest, ConvertToString) { EXPECT_EQ("abc", s); } -struct CountCalls { - int &num_calls; +TEST(FormatterTest, Ctor) { + fmt::Formatter<> f1("test"); + fmt::Formatter<> f1copy(f1); + fmt::Formatter<> f2("test", fmt::NullSink()); + fmt::Formatter f3("test"); + fmt::Formatter f4(L"test"); + fmt::Formatter f4copy(f4); + fmt::Formatter f5(L"test", fmt::NullSink()); +} - CountCalls(int &num_calls) : num_calls(num_calls) {} +// A sink that counts the number of times the output is written to it. +struct CountingSink { + int &num_writes; + + explicit CountingSink(int &num_calls) : num_writes(num_writes) {} void operator()(const Writer &) const { - ++num_calls; + ++num_writes; } }; -TEST(FormatterTest, Action) { - int num_calls = 0; +TEST(FormatterTest, Sink) { + int num_writes = 0; { - fmt::Formatter af("test", CountCalls(num_calls)); - EXPECT_EQ(0, num_calls); + fmt::Formatter f("test", CountingSink(num_writes)); + EXPECT_EQ(0, num_writes); } - EXPECT_EQ(1, num_calls); + EXPECT_EQ(1, num_writes); } -TEST(FormatterTest, ActionNotCalledOnError) { - int num_calls = 0; +TEST(FormatterTest, OutputNotWrittenOnError) { + int num_writes = 0; { - typedef fmt::Formatter TestFormatter; - EXPECT_THROW(TestFormatter af("{0", CountCalls(num_calls)), FormatError); + typedef fmt::Formatter TestFormatter; + EXPECT_THROW(TestFormatter f("{0", CountingSink(num_writes)), FormatError); } - EXPECT_EQ(0, num_calls); + EXPECT_EQ(0, num_writes); } // The test doesn't compile on older compilers which follow C++03 and @@ -1641,7 +1652,7 @@ class File { int fd() const { return fd_; } }; -TEST(ColorTest, PrintColored) { +TEST(FormatTest, PrintColored) { std::fflush(stdout); File saved_stdio(dup(1)); EXPECT_NE(-1, saved_stdio.fd()); @@ -1661,6 +1672,13 @@ TEST(ColorTest, PrintColored) { #endif +#if FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES +TEST(FormatTest, Variadic) { + EXPECT_EQ("Hello, world!1", str(Format("Hello, {}!{}", "world", 1))); + EXPECT_EQ(L"Hello, world!1", str(Format(L"Hello, {}!{}", L"world", 1))); +} +#endif // FMT_USE_VARIADIC_TEMPLATES + template std::string str(const T &value) { return fmt::str(fmt::Format("{0}") << value); @@ -1672,13 +1690,6 @@ TEST(StrTest, Convert) { EXPECT_EQ("2012-12-9", s); } -#if FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES -TEST(FormatTest, Variadic) { - EXPECT_EQ("Hello, world!1", str(Format("Hello, {}!{}", "world", 1))); - EXPECT_EQ(L"Hello, world!1", str(Format(L"Hello, {}!{}", L"world", 1))); -} -#endif // FMT_USE_VARIADIC_TEMPLATES - int main(int argc, char **argv) { #ifdef _WIN32 // Disable message boxes on assertion failures. diff --git a/format.cc b/format.cc index 6207fe57..485df7de 100644 --- a/format.cc +++ b/format.cc @@ -688,12 +688,13 @@ void fmt::BasicWriter::FormatParser::Format( writer.buffer_.append(start, s); } -void fmt::ColorWriter::operator()(const fmt::BasicWriter &w) const { +void fmt::ANSITerminalSink::operator()( + const fmt::BasicWriter &w) const { char escape[] = "\x1b[30m"; escape[3] = '0' + static_cast(color_); - std::fputs(escape, stdout); - std::fwrite(w.data(), 1, w.size(), stdout); - std::fputs(RESET_COLOR, stdout); + std::fputs(escape, file_); + std::fwrite(w.data(), 1, w.size(), file_); + std::fputs(RESET_COLOR, file_); } // Explicit instantiations for char. diff --git a/format.h b/format.h index 785cb539..d6cad8b7 100644 --- a/format.h +++ b/format.h @@ -805,13 +805,13 @@ class BasicWriter { }; }; - // Argument action that does nothing. - struct EmptyArgAction { + // An argument action that does nothing. + struct NullArgAction { void operator()() const {} }; // A wrapper around a format argument. - template + template class BasicArg : public Action, public ArgInfo { private: // This method is private to disallow formatting of arbitrary pointers. @@ -1039,7 +1039,7 @@ class BasicWriter { } /** - * Writes a character to the stream. + Writes a character to the stream. */ BasicWriter &operator<<(char value) { *GrowBuffer(1) = value; @@ -1239,7 +1239,7 @@ class BasicFormatter { // Here an Arg object wraps a temporary std::string which is destroyed at // the end of the full expression. Since the string object is constructed // before the Arg object, it will be destroyed after, so it will be alive - // in the Arg's destructor where the action is called. + // in the Arg's destructor where the action is invoked. // Note that the string object will not necessarily be alive when the // destructor of BasicFormatter is called. Otherwise we wouldn't need // this class. @@ -1368,41 +1368,41 @@ inline const wchar_t *c_str(internal::FormatterProxy p) { } /** - A formatting action that does nothing. + A sink that ignores output. */ -class NoAction { +class NullSink { public: - /** Does nothing. */ + /** Ignores the output. */ template void operator()(const BasicWriter &) const {} }; /** \rst - A formatter with an action performed when formatting is complete. - Objects of this class normally exist only as temporaries returned - by one of the formatting functions. You can use this class to create - your own functions similar to :cpp:func:`fmt::Format()`. + A formatter that sends output to a sink. Objects of this class normally + exist only as temporaries returned by one of the formatting functions. + You can use this class to create your own functions similar to + :cpp:func:`fmt::Format()`. **Example**:: - struct PrintError { + struct ErrorSink { void operator()(const fmt::Writer &w) const { fmt::Print("Error: {}\n") << w.str(); } }; // Formats an error message and prints it to stdout. - fmt::Formatter ReportError(const char *format) { - fmt::Formatter f(format); + fmt::Formatter ReportError(const char *format) { + fmt::Formatter f(format); return f; } ReportError("File not found: {}") << path; \endrst */ -template -class Formatter : private Action, public BasicFormatter { +template +class Formatter : private Sink, public BasicFormatter { private: BasicWriter writer_; bool inactive_; @@ -1412,26 +1412,44 @@ class Formatter : private Action, public BasicFormatter { public: /** \rst - Constructs a formatter with a format string and an action. - The action should be an unary function object that takes a const - reference to :cpp:class:`fmt::BasicWriter` as an argument. - See :cpp:class:`fmt::NoAction` and :cpp:class:`fmt::Write` for - examples of action classes. + Constructs a formatter with a format string and a sink. + The sink should be an unary function object that takes a const + reference to :cpp:class:`fmt::BasicWriter`, representing the + formatting output, as an argument. See :cpp:class:`fmt::NullSink` + and :cpp:class:`fmt::FileSink` for examples of sink classes. \endrst */ - explicit Formatter(BasicStringRef format, Action a = Action()) - : Action(a), BasicFormatter(writer_, format.c_str()), + explicit Formatter(BasicStringRef format, Sink s = Sink()) + : Sink(s), BasicFormatter(writer_, format.c_str()), inactive_(false) { } - Formatter(Formatter &f) - : Action(f), BasicFormatter(writer_, f.TakeFormatString()), - inactive_(false) { - f.inactive_ = true; - } - /** - Performs the actual formatting, invokes the action and destroys the object. + \rst + A "move" constructor. Constructs a formatter transferring the format + string from other to this object. This constructor is used to return + a formatter object from a formatting function since the copy constructor + taking a const reference is disabled to prevent misuse of the API. + It is not implemented as a move constructor for compatibility with + pre-C++11 compilers, but should be treated as such. + + **Example**:: + + fmt::Formatter<> Format(fmt::StringRef format) { + fmt::Formatter<> f(format); + return f; + } + \endrst + */ + Formatter(Formatter &other) + : Sink(other), BasicFormatter(writer_, other.TakeFormatString()), + inactive_(false) { + other.inactive_ = true; + } + + /** + Performs the formatting, sends the output to the sink and destroys + the object. */ ~Formatter() FMT_NOEXCEPT(false) { if (!inactive_) { @@ -1441,6 +1459,118 @@ class Formatter : private Action, public BasicFormatter { } }; +/** + \rst + Formats a string similarly to Python's `str.format + `__. + Returns a temporary formatter object that accepts arguments via + operator ``<<``. + + *format* is a format string that contains literal text and replacement + fields surrounded by braces ``{}``. The formatter object replaces the + fields with formatted arguments and stores the output in a memory buffer. + The content of the buffer can be converted to ``std::string`` with + :cpp:func:`fmt::str()` or accessed as a C string with + :cpp:func:`fmt::c_str()`. + + **Example**:: + + std::string message = str(Format("The answer is {}") << 42); + + See also `Format String Syntax`_. + \endrst +*/ +inline Formatter<> Format(StringRef format) { + Formatter<> f(format); + return f; +} + +inline Formatter Format(WStringRef format) { + Formatter f(format); + return f; +} + +/** A sink that writes output to a file. */ +class FileSink { + private: + std::FILE *file_; + + public: + FileSink(std::FILE *f) : file_(f) {} + + /** Writes the output to a file. */ + void operator()(const BasicWriter &w) const { + // TODO: check error + std::fwrite(w.data(), w.size(), 1, file_); + } +}; + +// Formats a string and prints it to stdout. +// Example: +// Print("Elapsed time: {0:.2f} seconds") << 1.23; +// TODO: wchar overload +inline Formatter Print(StringRef format) { + Formatter f(format, stdout); + return f; +} + +enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; + +/** + A sink that writes output to a terminal using ANSI escape sequences + to specify color. + */ +class ANSITerminalSink { + private: + std::FILE *file_; + Color color_; + + public: + ANSITerminalSink(std::FILE *f, Color c) : file_(f), color_(c) {} + + /** + Writes the output to a terminal using ANSI escape sequences to + specify color. + */ + void operator()(const BasicWriter &w) const; +}; + +/** + Formats a string and prints it to stdout using ANSI escape sequences + to specify color (experimental). + Example: + PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; + */ +inline Formatter PrintColored(Color c, StringRef format) { + Formatter f(format, ANSITerminalSink(stdout, c)); + return f; +} + +#if FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES + +template +inline Writer Format(const StringRef &format, const Args & ... args) { + Writer w; + w.Format(format, args...); + return std::move(w); +} + +template +inline WWriter Format(const WStringRef &format, const Args & ... args) { + WWriter w; + w.Format(format, args...); + return std::move(w); +} + +template +void Print(StringRef format, const Args & ... args) { + Writer w; + w.Format(format, args...); + std::fwrite(w.data(), 1, w.size(), stdout); +} + +#endif // FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES + /** Fast integer formatter. */ @@ -1473,7 +1603,7 @@ class FormatInt { *--buffer_end = internal::DIGITS[index]; return buffer_end; } - + void FormatSigned(LongLong value) { ULongLong abs_value = value; bool negative = value < 0; @@ -1542,92 +1672,6 @@ inline void FormatDec(char *&buffer, T value) { internal::FormatDecimal(buffer, abs_value, num_digits); buffer += num_digits; } - -/** - \rst - Formats a string similarly to Python's `str.format - `__. - Returns a temporary formatter object that accepts arguments via - operator ``<<``. - - *format* is a format string that contains literal text and replacement - fields surrounded by braces ``{}``. The formatter object replaces the - fields with formatted arguments and stores the output in a memory buffer. - The content of the buffer can be converted to ``std::string`` with - :cpp:func:`fmt::str()` or accessed as a C string with - :cpp:func:`fmt::c_str()`. - - **Example**:: - - std::string message = str(Format("The answer is {}") << 42); - - See also `Format String Syntax`_. - \endrst -*/ -inline Formatter<> Format(StringRef format) { - Formatter<> f(format); - return f; -} - -inline Formatter Format(WStringRef format) { - Formatter f(format); - return f; -} - -/** A formatting action that writes formatted output to stdout. */ -class Write { - public: - /** Writes the output to stdout. */ - void operator()(const BasicWriter &w) const { - std::fwrite(w.data(), 1, w.size(), stdout); - } -}; - -// Formats a string and prints it to stdout. -// Example: -// Print("Elapsed time: {0:.2f} seconds") << 1.23; -inline Formatter Print(StringRef format) { - Formatter f(format); - return f; -} - -enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; - -/** A formatting action that writes colored output to stdout. */ -class ColorWriter { - private: - Color color_; - - public: - explicit ColorWriter(Color c) : color_(c) {} - - /** Writes the colored output to stdout. */ - void operator()(const BasicWriter &w) const; -}; - -// Formats a string and prints it to stdout with the given color. -// Example: -// PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; -inline Formatter PrintColored(Color c, StringRef format) { - Formatter f(format, ColorWriter(c)); - return f; -} - -#if FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES -template -inline Writer Format(const StringRef &format, const Args & ... args) { - Writer w; - w.Format(format, args...); - return std::move(w); -} - -template -inline WWriter Format(const WStringRef &format, const Args & ... args) { - WWriter w; - w.Format(format, args...); - return std::move(w); -} -#endif // FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES } // Restore warnings.