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);
}
struct CountCalls {
int &num_calls;
TEST(FormatterTest, Ctor) {
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 {
++num_calls;
++num_writes;
}
};
TEST(FormatterTest, Action) {
int num_calls = 0;
TEST(FormatterTest, Sink) {
int num_writes = 0;
{
fmt::Formatter<CountCalls> af("test", CountCalls(num_calls));
EXPECT_EQ(0, num_calls);
fmt::Formatter<CountingSink> 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<CountCalls> TestFormatter;
EXPECT_THROW(TestFormatter af("{0", CountCalls(num_calls)), FormatError);
typedef fmt::Formatter<CountingSink> 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 <typename T>
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.

View File

@ -688,12 +688,13 @@ void fmt::BasicWriter<Char>::FormatParser::Format(
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";
escape[3] = '0' + static_cast<char>(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.

280
format.h
View File

@ -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 <typename Action = EmptyArgAction>
template <typename Action = NullArgAction>
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<wchar_t> p) {
}
/**
A formatting action that does nothing.
A sink that ignores output.
*/
class NoAction {
class NullSink {
public:
/** Does nothing. */
/** Ignores the output. */
template <typename Char>
void operator()(const BasicWriter<Char> &) 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<PrintError> ReportError(const char *format) {
fmt::Formatter f<PrintError>(format);
fmt::Formatter<ErrorSink> ReportError(const char *format) {
fmt::Formatter f<ErrorSink>(format);
return f;
}
ReportError("File not found: {}") << path;
\endrst
*/
template <typename Action = NoAction, typename Char = char>
class Formatter : private Action, public BasicFormatter<Char> {
template <typename Sink = NullSink, typename Char = char>
class Formatter : private Sink, public BasicFormatter<Char> {
private:
BasicWriter<Char> writer_;
bool inactive_;
@ -1412,26 +1412,44 @@ class Formatter : private Action, public BasicFormatter<Char> {
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<Char> format, Action a = Action())
: Action(a), BasicFormatter<Char>(writer_, format.c_str()),
explicit Formatter(BasicStringRef<Char> format, Sink s = Sink())
: Sink(s), BasicFormatter<Char>(writer_, format.c_str()),
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) {
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.
*/
@ -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
<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.