Refactor action classes, Action -> Sink, add comments.

This commit is contained in:
Victor Zverovich 2014-04-28 08:59:29 -07:00
parent d2bf073334
commit d9db89814f
3 changed files with 200 additions and 144 deletions

View File

@ -1479,32 +1479,43 @@ TEST(StringRefTest, ConvertToString) {
EXPECT_EQ("abc", s); EXPECT_EQ("abc", s);
} }
struct CountCalls { TEST(FormatterTest, Ctor) {
int &num_calls; fmt::Formatter<> f1("test");
fmt::Formatter<> f1copy(f1);
fmt::Formatter<> f2("test", fmt::NullSink());
fmt::Formatter<fmt::NullSink> f3("test");
fmt::Formatter<fmt::NullSink, wchar_t> f4(L"test");
fmt::Formatter<fmt::NullSink, wchar_t> f4copy(f4);
fmt::Formatter<fmt::NullSink, wchar_t> 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 { void operator()(const Writer &) const {
++num_calls; ++num_writes;
} }
}; };
TEST(FormatterTest, Action) { TEST(FormatterTest, Sink) {
int num_calls = 0; int num_writes = 0;
{ {
fmt::Formatter<CountCalls> af("test", CountCalls(num_calls)); fmt::Formatter<CountingSink> f("test", CountingSink(num_writes));
EXPECT_EQ(0, num_calls); EXPECT_EQ(0, num_writes);
} }
EXPECT_EQ(1, num_calls); EXPECT_EQ(1, num_writes);
} }
TEST(FormatterTest, ActionNotCalledOnError) { TEST(FormatterTest, OutputNotWrittenOnError) {
int num_calls = 0; int num_writes = 0;
{ {
typedef fmt::Formatter<CountCalls> TestFormatter; typedef fmt::Formatter<CountingSink> TestFormatter;
EXPECT_THROW(TestFormatter af("{0", CountCalls(num_calls)), FormatError); 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 // The test doesn't compile on older compilers which follow C++03 and
@ -1641,7 +1652,7 @@ class File {
int fd() const { return fd_; } int fd() const { return fd_; }
}; };
TEST(ColorTest, PrintColored) { TEST(FormatTest, PrintColored) {
std::fflush(stdout); std::fflush(stdout);
File saved_stdio(dup(1)); File saved_stdio(dup(1));
EXPECT_NE(-1, saved_stdio.fd()); EXPECT_NE(-1, saved_stdio.fd());
@ -1661,6 +1672,13 @@ TEST(ColorTest, PrintColored) {
#endif #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 <typename T> template <typename T>
std::string str(const T &value) { std::string str(const T &value) {
return fmt::str(fmt::Format("{0}") << value); return fmt::str(fmt::Format("{0}") << value);
@ -1672,13 +1690,6 @@ TEST(StrTest, Convert) {
EXPECT_EQ("2012-12-9", s); 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) { int main(int argc, char **argv) {
#ifdef _WIN32 #ifdef _WIN32
// Disable message boxes on assertion failures. // Disable message boxes on assertion failures.

View File

@ -688,12 +688,13 @@ void fmt::BasicWriter<Char>::FormatParser::Format(
writer.buffer_.append(start, s); writer.buffer_.append(start, s);
} }
void fmt::ColorWriter::operator()(const fmt::BasicWriter<char> &w) const { void fmt::ANSITerminalSink::operator()(
const fmt::BasicWriter<char> &w) const {
char escape[] = "\x1b[30m"; char escape[] = "\x1b[30m";
escape[3] = '0' + static_cast<char>(color_); escape[3] = '0' + static_cast<char>(color_);
std::fputs(escape, stdout); std::fputs(escape, file_);
std::fwrite(w.data(), 1, w.size(), stdout); std::fwrite(w.data(), 1, w.size(), file_);
std::fputs(RESET_COLOR, stdout); std::fputs(RESET_COLOR, file_);
} }
// Explicit instantiations for char. // Explicit instantiations for char.

280
format.h
View File

@ -805,13 +805,13 @@ class BasicWriter {
}; };
}; };
// Argument action that does nothing. // An argument action that does nothing.
struct EmptyArgAction { struct NullArgAction {
void operator()() const {} void operator()() const {}
}; };
// A wrapper around a format argument. // A wrapper around a format argument.
template <typename Action = EmptyArgAction> template <typename Action = NullArgAction>
class BasicArg : public Action, public ArgInfo { class BasicArg : public Action, public ArgInfo {
private: private:
// This method is private to disallow formatting of arbitrary pointers. // 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) { BasicWriter &operator<<(char value) {
*GrowBuffer(1) = value; *GrowBuffer(1) = value;
@ -1239,7 +1239,7 @@ class BasicFormatter {
// Here an Arg object wraps a temporary std::string which is destroyed at // 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 // 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 // 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 // Note that the string object will not necessarily be alive when the
// destructor of BasicFormatter is called. Otherwise we wouldn't need // destructor of BasicFormatter is called. Otherwise we wouldn't need
// this class. // this class.
@ -1368,41 +1368,41 @@ inline const wchar_t *c_str(internal::FormatterProxy<wchar_t> p) {
} }
/** /**
A formatting action that does nothing. A sink that ignores output.
*/ */
class NoAction { class NullSink {
public: public:
/** Does nothing. */ /** Ignores the output. */
template <typename Char> template <typename Char>
void operator()(const BasicWriter<Char> &) const {} void operator()(const BasicWriter<Char> &) const {}
}; };
/** /**
\rst \rst
A formatter with an action performed when formatting is complete. A formatter that sends output to a sink. Objects of this class normally
Objects of this class normally exist only as temporaries returned exist only as temporaries returned by one of the formatting functions.
by one of the formatting functions. You can use this class to create You can use this class to create your own functions similar to
your own functions similar to :cpp:func:`fmt::Format()`. :cpp:func:`fmt::Format()`.
**Example**:: **Example**::
struct PrintError { struct ErrorSink {
void operator()(const fmt::Writer &w) const { void operator()(const fmt::Writer &w) const {
fmt::Print("Error: {}\n") << w.str(); fmt::Print("Error: {}\n") << w.str();
} }
}; };
// Formats an error message and prints it to stdout. // Formats an error message and prints it to stdout.
fmt::Formatter<PrintError> ReportError(const char *format) { fmt::Formatter<ErrorSink> ReportError(const char *format) {
fmt::Formatter f<PrintError>(format); fmt::Formatter f<ErrorSink>(format);
return f; return f;
} }
ReportError("File not found: {}") << path; ReportError("File not found: {}") << path;
\endrst \endrst
*/ */
template <typename Action = NoAction, typename Char = char> template <typename Sink = NullSink, typename Char = char>
class Formatter : private Action, public BasicFormatter<Char> { class Formatter : private Sink, public BasicFormatter<Char> {
private: private:
BasicWriter<Char> writer_; BasicWriter<Char> writer_;
bool inactive_; bool inactive_;
@ -1412,26 +1412,44 @@ class Formatter : private Action, public BasicFormatter<Char> {
public: public:
/** /**
\rst \rst
Constructs a formatter with a format string and an action. Constructs a formatter with a format string and a sink.
The action should be an unary function object that takes a const The sink should be an unary function object that takes a const
reference to :cpp:class:`fmt::BasicWriter` as an argument. reference to :cpp:class:`fmt::BasicWriter`, representing the
See :cpp:class:`fmt::NoAction` and :cpp:class:`fmt::Write` for formatting output, as an argument. See :cpp:class:`fmt::NullSink`
examples of action classes. and :cpp:class:`fmt::FileSink` for examples of sink classes.
\endrst \endrst
*/ */
explicit Formatter(BasicStringRef<Char> format, Action a = Action()) explicit Formatter(BasicStringRef<Char> format, Sink s = Sink())
: Action(a), BasicFormatter<Char>(writer_, format.c_str()), : Sink(s), BasicFormatter<Char>(writer_, format.c_str()),
inactive_(false) { inactive_(false) {
} }
Formatter(Formatter &f)
: Action(f), BasicFormatter<Char>(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<Char>(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) { ~Formatter() FMT_NOEXCEPT(false) {
if (!inactive_) { if (!inactive_) {
@ -1441,6 +1459,118 @@ class Formatter : private Action, public BasicFormatter<Char> {
} }
}; };
/**
\rst
Formats a string similarly to Python's `str.format
<http://docs.python.org/3/library/stdtypes.html#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<NullSink, wchar_t> Format(WStringRef format) {
Formatter<NullSink, wchar_t> 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<char> &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<FileSink> Print(StringRef format) {
Formatter<FileSink> 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<char> &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<ANSITerminalSink> PrintColored(Color c, StringRef format) {
Formatter<ANSITerminalSink> f(format, ANSITerminalSink(stdout, c));
return f;
}
#if FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES
template<typename... Args>
inline Writer Format(const StringRef &format, const Args & ... args) {
Writer w;
w.Format(format, args...);
return std::move(w);
}
template<typename... Args>
inline WWriter Format(const WStringRef &format, const Args & ... args) {
WWriter w;
w.Format(format, args...);
return std::move(w);
}
template<typename... Args>
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. Fast integer formatter.
*/ */
@ -1473,7 +1603,7 @@ class FormatInt {
*--buffer_end = internal::DIGITS[index]; *--buffer_end = internal::DIGITS[index];
return buffer_end; return buffer_end;
} }
void FormatSigned(LongLong value) { void FormatSigned(LongLong value) {
ULongLong abs_value = value; ULongLong abs_value = value;
bool negative = value < 0; bool negative = value < 0;
@ -1542,92 +1672,6 @@ inline void FormatDec(char *&buffer, T value) {
internal::FormatDecimal(buffer, abs_value, num_digits); internal::FormatDecimal(buffer, abs_value, num_digits);
buffer += num_digits; buffer += num_digits;
} }
/**
\rst
Formats a string similarly to Python's `str.format
<http://docs.python.org/3/library/stdtypes.html#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<NoAction, wchar_t> Format(WStringRef format) {
Formatter<NoAction, wchar_t> 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<char> &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<Write> Print(StringRef format) {
Formatter<Write> 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<char> &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<ColorWriter> PrintColored(Color c, StringRef format) {
Formatter<ColorWriter> f(format, ColorWriter(c));
return f;
}
#if FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES
template<typename... Args>
inline Writer Format(const StringRef &format, const Args & ... args) {
Writer w;
w.Format(format, args...);
return std::move(w);
}
template<typename... Args>
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. // Restore warnings.