BasicArgFormatter -> ArgFormatter. FullFormat -> ActiveFormatter. Use ActiveFormatter to implement Format and Print.

This commit is contained in:
Victor Zverovich 2012-12-11 13:54:53 -08:00
parent 57dbd2c3fe
commit 87b5ebfc4a
4 changed files with 114 additions and 70 deletions

View File

@ -46,6 +46,24 @@ An object of any user-defined type for which there is an overloaded
std::string s = str(fmt::Format("The date is {0}") << Date(2012, 12, 9));
// s == "The date is 2012-12-9"
You can use ``fmt::ActiveFormatter`` to create your own functions
similar to ``fmt::Format`` and ``fmt::Print`` with an arbitrary action
performed when formatting is complete:
struct PrintError {
void operator()(const fmt::Formatter &f) const {
std::cerr << "Error: " << f.str() << std::endl;
}
};
// Formats an error message and prints it to std::cerr.
fmt::ActiveFormatter<PrintError> ReportError(const char *format) {
fmt::ActiveFormatter<PrintError> af(format);
return af;
}
ReportError("File not found: {0}") << path;
Format string syntax
--------------------

View File

@ -366,8 +366,3 @@ void fmt::Formatter::DoFormat() {
buffer_.append(start, s + 1);
buffer_.resize(buffer_.size() - 1); // Don't count the terminating zero.
}
fmt::BasicArgFormatter::~BasicArgFormatter() {
if (!formatter_) return;
FinishFormatting();
}

111
format.h
View File

@ -94,7 +94,7 @@ class FormatError : public std::runtime_error {
FormatError(const std::string &message) : std::runtime_error(message) {}
};
class BasicArgFormatter;
class ArgFormatter;
// Formatter provides string formatting functionality similar to Python's
// str.format. The output is stored in a memory buffer that grows dynamically.
@ -183,17 +183,17 @@ class Formatter {
format(&Formatter::FormatCustomArg<T>) {}
~Arg() {
// Format is called here to make sure that the argument object
// referred to is still alive, for example:
// Format is called here to make sure that a referred object is
// still alive, for example:
//
// Print("{0}") << std::string("test");
//
// Here an Arg object refers to a temporary std::string which is
// destroyed at the end of the statement. 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 when Format is called.
// 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 BasicArgFormatter is called.
// the destructor of ArgFormatter is called.
formatter->Format();
}
};
@ -203,7 +203,7 @@ class Formatter {
const char *format_; // Format string.
friend class BasicArgFormatter;
friend class ArgFormatter;
void Add(const Arg &arg) {
args_.push_back(&arg);
@ -238,36 +238,38 @@ class Formatter {
}
public:
Formatter() : format_(0) {}
Formatter() : format_(0) { buffer_[0] = 0; }
// Formats a string appending the output to the internal buffer.
// Arguments are accepted through the returned BasicArgFormatter object
// Arguments are accepted through the returned ArgFormatter object
// using inserter operator<<.
BasicArgFormatter operator()(const char *format);
ArgFormatter operator()(const char *format);
std::size_t size() const { return buffer_.size(); }
const char *data() const { return &buffer_[0]; }
const char *c_str() const { return &buffer_[0]; }
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 operators <<
// that feed arguments to the formatter.
class BasicArgFormatter {
// 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_;
BasicArgFormatter(BasicArgFormatter& other)
ArgFormatter(ArgFormatter& other)
: formatter_(other.formatter_) {
other.formatter_ = 0;
}
BasicArgFormatter& operator=(const BasicArgFormatter& other) {
ArgFormatter& operator=(const ArgFormatter& other) {
formatter_ = other.formatter_;
other.formatter_ = 0;
return *this;
@ -283,33 +285,24 @@ class BasicArgFormatter {
}
public:
explicit BasicArgFormatter(Formatter &f) : formatter_(&f) {}
~BasicArgFormatter();
explicit ArgFormatter(Formatter &f) : formatter_(&f) {}
~ArgFormatter() { FinishFormatting(); }
BasicArgFormatter &operator<<(const Formatter::Arg &arg) {
// Feeds an argument to a formatter.
ArgFormatter &operator<<(const Formatter::Arg &arg) {
arg.formatter = formatter_;
formatter_->Add(arg);
return *this;
}
friend const char *c_str(const BasicArgFormatter &af) {
// Performs formatting and returns a C string with the output.
friend const char *c_str(const ArgFormatter &af) {
return af.FinishFormatting()->c_str();
}
friend std::string str(const BasicArgFormatter &af) {
return af.FinishFormatting()->c_str();
}
};
template <typename Callback>
class ArgFormatter : public BasicArgFormatter {
public:
explicit ArgFormatter(Formatter &f) : BasicArgFormatter(f) {}
~ArgFormatter() {
if (!formatter_) return;
Callback callback;
callback(*formatter_);
// Performs formatting and returns a std::string with the output.
friend std::string str(const ArgFormatter &af) {
return af.FinishFormatting()->str();
}
};
@ -325,55 +318,53 @@ void Formatter::FormatCustomArg(const void *arg, int width) {
std::fill_n(out + str.size(), width - str.size(), ' ');
}
inline BasicArgFormatter Formatter::operator()(const char *format) {
BasicArgFormatter formatter(*this);
inline ArgFormatter Formatter::operator()(const char *format) {
ArgFormatter formatter(*this);
format_ = format;
args_.clear();
return formatter;
}
class FullFormat : public BasicArgFormatter {
// A formatter with an action performed when formatting is complete.
template <typename Action>
class ActiveFormatter : public ArgFormatter {
private:
mutable Formatter formatter_;
// Do not implement.
FullFormat& operator=(const FullFormat&);
ActiveFormatter& operator=(const ActiveFormatter&);
public:
explicit FullFormat(const char *format) : BasicArgFormatter(formatter_) {
BasicArgFormatter::operator=(formatter_(format));
explicit ActiveFormatter(const char *format) : ArgFormatter(formatter_) {
ArgFormatter::operator=(formatter_(format));
}
FullFormat(FullFormat& other) : BasicArgFormatter(other) {}
ActiveFormatter(ActiveFormatter& other) : ArgFormatter(other) {}
~FullFormat() {
FinishFormatting();
~ActiveFormatter() {
Action()(*FinishFormatting());
}
};
inline FullFormat Format(const char *format) {
FullFormat ff(format);
return ff;
struct Ignore {
void operator()(Formatter &) const {}
};
inline ActiveFormatter<Ignore> Format(const char *format) {
ActiveFormatter<Ignore> af(format);
return af;
}
class Print : public BasicArgFormatter {
private:
Formatter formatter_;
// Do not implement.
Print(const Print&);
Print& operator=(const Print&);
public:
explicit Print(const char *format) : BasicArgFormatter(formatter_) {
BasicArgFormatter::operator=(formatter_(format));
}
~Print() {
FinishFormatting();
std::fwrite(formatter_.data(), 1, formatter_.size(), stdout);
struct Write {
void operator()(Formatter &f) const {
std::fwrite(f.data(), 1, f.size(), stdout);
}
};
inline ActiveFormatter<Write> Print(const char *format) {
ActiveFormatter<Write> af(format);
return af;
}
}
namespace fmt = format;

View File

@ -525,18 +525,58 @@ TEST(FormatterTest, FormatStringFromSpeedTest) {
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::BasicArgFormatter &af = fmt::Format("{0}");
const_cast<fmt::BasicArgFormatter&>(af) << std::string("test");
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, Formatter) {
TEST(FormatterTest, FormatterCtor) {
Formatter format;
EXPECT_EQ(0, format.size());
EXPECT_STREQ("", format.data());
EXPECT_STREQ("", format.c_str());
EXPECT_EQ("", format.str());
format("part{0}") << 1;
format("part{0}") << 2;
EXPECT_EQ("part1part2", format.str());
}
TEST(FormatterTest, FormatterAppend) {
Formatter format;
format("part{0}") << 1;
EXPECT_EQ(strlen("part1"), format.size());
EXPECT_STREQ("part1", format.c_str());
EXPECT_STREQ("part1", format.data());
EXPECT_EQ("part1", format.str());
format("part{0}") << 2;
EXPECT_EQ(strlen("part1part2"), format.size());
EXPECT_STREQ("part1part2", format.c_str());
EXPECT_STREQ("part1part2", format.data());
EXPECT_EQ("part1part2", format.str());
}
TEST(FormatterTest, FormatterExample) {
Formatter format;
format("Current point:\n");
format("({0:+f}, {1:+f})\n") << -3.14 << 3.14;
EXPECT_STREQ("Current point:\n(-3.140000, +3.140000)\n", format.c_str());
EXPECT_EQ("Current point:\n(-3.140000, +3.140000)\n", format.str());
}
// TODO: test API
struct PrintError {
void operator()(const fmt::Formatter &f) const {
std::cerr << "Error: " << f.str() << std::endl;
}
};
fmt::ActiveFormatter<PrintError> ReportError(const char *format) {
fmt::ActiveFormatter<PrintError> af(format);
return af;
}
TEST(FormatterTest, ArgFormatter) {
std::string path = "somefile";
ReportError("File not found: {0}") << path;
}
// TODO: test ArgFormatter