ArgFormatter -> Formatter::ArgInserter. Test ArgInserter and ActiveFormatter.

This commit is contained in:
Victor Zverovich 2012-12-11 16:26:04 -08:00
parent 1f6e4e9d7a
commit e2725eeeb1
2 changed files with 123 additions and 82 deletions

155
format.h
View File

@ -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 <typename Action>
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 <typename T>
void Formatter::FormatCustomArg(const void *arg, int width) {
const T &value = *static_cast<const T*>(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 <typename Action>
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<Ignore> Format(const char *format) {
ActiveFormatter<Ignore> 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<Write> Print(const char *format) {
ActiveFormatter<Write> af(format);
return af;

View File

@ -522,16 +522,6 @@ TEST(FormatterTest, FormatStringFromSpeedTest) {
<< reinterpret_cast<void*>(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<fmt::ArgFormatter&>(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<CallCheck> 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<fmt::Ignore> &af = fmt::Format("{0}");
const_cast<fmt::ActiveFormatter<fmt::Ignore>&>(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<PrintError> 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