From e2725eeeb12dfc8104e8e0cfc43980264d153376 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 11 Dec 2012 16:26:04 -0800 Subject: [PATCH] ArgFormatter -> Formatter::ArgInserter. Test ArgInserter and ActiveFormatter. --- format.h | 155 +++++++++++++++++++++++++++---------------------- format_test.cc | 50 +++++++++++----- 2 files changed, 123 insertions(+), 82 deletions(-) diff --git a/format.h b/format.h index 46cce6f1..588a5d0a 100644 --- a/format.h +++ b/format.h @@ -94,8 +94,6 @@ class FormatError : public std::runtime_error { FormatError(const std::string &message) : std::runtime_error(message) {} }; -class ArgFormatter; - // Formatter provides string formatting functionality similar to Python's // str.format. The output is stored in a memory buffer that grows dynamically. // Usage: @@ -193,7 +191,7 @@ class Formatter { // constructed before the Arg object, it will be destroyed after, // so it will be alive in the Arg's destructor where Format is called. // Note that the string object will not necessarily be alive when - // the destructor of ArgFormatter is called. + // the destructor of ArgInserter is called. formatter->Format(); } }; @@ -203,7 +201,65 @@ class Formatter { const char *format_; // Format string. - friend class ArgFormatter; + template + friend class ActiveFormatter; + + // This is a transient object that normally exists only as a temporary + // returned by one of the formatting functions. It stores a reference + // to a formatter and provides operator<< that feeds arguments to the + // formatter. + class ArgInserter { + private: + mutable Formatter *formatter_; + + friend class Formatter; + + protected: + explicit ArgInserter(Formatter *f = 0) : formatter_(f) {} + + ArgInserter(ArgInserter& other) + : formatter_(other.formatter_) { + other.formatter_ = 0; + } + + ArgInserter& operator=(const ArgInserter& other) { + formatter_ = other.formatter_; + other.formatter_ = 0; + return *this; + } + + const Formatter *Format() const { + Formatter *f = formatter_; + if (f) { + formatter_ = 0; + f->Format(); + } + return f; + } + + public: + ~ArgInserter() { + if (formatter_) + formatter_->Format(); + } + + // Feeds an argument to a formatter. + ArgInserter &operator<<(const Formatter::Arg &arg) { + arg.formatter = formatter_; + formatter_->Add(arg); + return *this; + } + + // Performs formatting and returns a C string with the output. + friend const char *c_str(const ArgInserter &af) { + return af.Format()->c_str(); + } + + // Performs formatting and returns a std::string with the output. + friend std::string str(const ArgInserter &af) { + return af.Format()->str(); + } + }; void Add(const Arg &arg) { args_.push_back(&arg); @@ -241,9 +297,9 @@ class Formatter { Formatter() : format_(0) { buffer_[0] = 0; } // Formats a string appending the output to the internal buffer. - // Arguments are accepted through the returned ArgFormatter object + // Arguments are accepted through the returned ArgInserter object // using inserter operator<<. - ArgFormatter operator()(const char *format); + ArgInserter operator()(const char *format); std::size_t size() const { return buffer_.size(); } @@ -253,59 +309,6 @@ class Formatter { std::string str() const { return std::string(&buffer_[0], buffer_.size()); } }; -// Argument formatter. This is a transient object that normally exists -// only as a temporary returned by one of the formatting functions. -// It stores a reference to a formatter and provides operator<< -// that feeds arguments to the formatter. -class ArgFormatter { - private: - friend class Formatter; - - protected: - mutable Formatter *formatter_; - - ArgFormatter(ArgFormatter& other) - : formatter_(other.formatter_) { - other.formatter_ = 0; - } - - ArgFormatter& operator=(const ArgFormatter& other) { - formatter_ = other.formatter_; - other.formatter_ = 0; - return *this; - } - - Formatter *FinishFormatting() const { - Formatter *f = formatter_; - if (f) { - formatter_ = 0; - f->Format(); - } - return f; - } - - public: - explicit ArgFormatter(Formatter &f) : formatter_(&f) {} - ~ArgFormatter() { FinishFormatting(); } - - // Feeds an argument to a formatter. - ArgFormatter &operator<<(const Formatter::Arg &arg) { - arg.formatter = formatter_; - formatter_->Add(arg); - return *this; - } - - // Performs formatting and returns a C string with the output. - friend const char *c_str(const ArgFormatter &af) { - return af.FinishFormatting()->c_str(); - } - - // Performs formatting and returns a std::string with the output. - friend std::string str(const ArgFormatter &af) { - return af.FinishFormatting()->str(); - } -}; - template void Formatter::FormatCustomArg(const void *arg, int width) { const T &value = *static_cast(arg); @@ -318,8 +321,8 @@ void Formatter::FormatCustomArg(const void *arg, int width) { std::fill_n(out + str.size(), width - str.size(), ' '); } -inline ArgFormatter Formatter::operator()(const char *format) { - ArgFormatter formatter(*this); +inline Formatter::ArgInserter Formatter::operator()(const char *format) { + ArgInserter formatter(this); format_ = format; args_.clear(); return formatter; @@ -327,40 +330,54 @@ inline ArgFormatter Formatter::operator()(const char *format) { // A formatter with an action performed when formatting is complete. template -class ActiveFormatter : public ArgFormatter { +class ActiveFormatter : public Formatter::ArgInserter { private: - mutable Formatter formatter_; + Formatter formatter_; + Action action_; // Do not implement. ActiveFormatter& operator=(const ActiveFormatter&); public: - explicit ActiveFormatter(const char *format) : ArgFormatter(formatter_) { - ArgFormatter::operator=(formatter_(format)); + // Creates an active formatter with a format string and an action. + // Action should be an unary function object that takes a const + // reference to Formatter as an argument. See Ignore and Write + // for examples of action classes. + explicit ActiveFormatter(const char *format, Action a = Action()) + : action_(a) { + ArgInserter::operator=(formatter_(format)); } - ActiveFormatter(ActiveFormatter& other) : ArgFormatter(other) {} + ActiveFormatter(ActiveFormatter& other) : ArgInserter(other) {} ~ActiveFormatter() { - Action()(*FinishFormatting()); + action_(*Format()); } }; +// A formatting action that does nothing. struct Ignore { - void operator()(Formatter &) const {} + void operator()(const Formatter &) const {} }; +// Formats a string. +// Example: +// std::string s = str(Format("Elapsed time: {0:.2f} seconds") << 1.23); inline ActiveFormatter Format(const char *format) { ActiveFormatter af(format); return af; } +// A formatting action that writes formatted output to stdout. struct Write { - void operator()(Formatter &f) const { + void operator()(const Formatter &f) const { std::fwrite(f.data(), 1, f.size(), stdout); } }; +// Formats a string and prints it to stdout. +// Example: +// Print("Elapsed time: {0:.2f} seconds") << 1.23; inline ActiveFormatter Print(const char *format) { ActiveFormatter af(format); return af; diff --git a/format_test.cc b/format_test.cc index 34826e2f..0a8a20aa 100644 --- a/format_test.cc +++ b/format_test.cc @@ -522,16 +522,6 @@ TEST(FormatterTest, FormatStringFromSpeedTest) { << reinterpret_cast(1000) << 'X')); } -TEST(FormatterTest, ArgLifetime) { - // The following code is for testing purposes only. It is a definite abuse - // of the API and shouldn't be used in real applications. - const fmt::ArgFormatter &af = fmt::Format("{0}"); - const_cast(af) << std::string("test"); - // String object passed as an argument to Print has been destroyed, - // but BasicArgFormatter dtor hasn't been called yet. - EXPECT_EQ("test", str(af)); -} - TEST(FormatterTest, FormatterCtor) { Formatter format; EXPECT_EQ(0, format.size()); @@ -564,9 +554,44 @@ TEST(FormatterTest, FormatterExample) { EXPECT_EQ("Current point:\n(-3.140000, +3.140000)\n", format.str()); } +TEST(FormatterTest, ArgInserter) { + Formatter format; + EXPECT_EQ("1", str(format("{0}") << 1)); + EXPECT_STREQ("12", c_str(format("{0}") << 2)); +} + +struct CallCheck { + bool &called; + + CallCheck(bool &called) : called(called) {} + + void operator()(const Formatter &) const { + called = true; + } +}; + +TEST(ActiveFormatterTest, Action) { + bool called = false; + { + fmt::ActiveFormatter af("test", CallCheck(called)); + EXPECT_FALSE(called); + } + EXPECT_TRUE(called); +} + +TEST(ActiveFormatterTest, ArgLifetime) { + // The following code is for testing purposes only. It is a definite abuse + // of the API and shouldn't be used in real applications. + const fmt::ActiveFormatter &af = fmt::Format("{0}"); + const_cast&>(af) << std::string("test"); + // String object passed as an argument to Print has been destroyed, + // but ArgInserter dtor hasn't been called yet. + EXPECT_EQ("test", str(af)); +} + struct PrintError { void operator()(const fmt::Formatter &f) const { - std::cerr << "Error: " << f.str() << std::endl; + //std::cerr << "Error: " << f.str() << std::endl; } }; @@ -575,8 +600,7 @@ fmt::ActiveFormatter ReportError(const char *format) { return af; } -TEST(FormatterTest, ArgFormatter) { +TEST(ActiveFormatterTest, Example) { std::string path = "somefile"; ReportError("File not found: {0}") << path; } -// TODO: test ArgFormatter