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)); std::string s = str(fmt::Format("The date is {0}") << Date(2012, 12, 9));
// s == "The date is 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 Format string syntax
-------------------- --------------------

View File

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

View File

@ -525,18 +525,58 @@ TEST(FormatterTest, FormatStringFromSpeedTest) {
TEST(FormatterTest, ArgLifetime) { TEST(FormatterTest, ArgLifetime) {
// The following code is for testing purposes only. It is a definite abuse // The following code is for testing purposes only. It is a definite abuse
// of the API and shouldn't be used in real applications. // of the API and shouldn't be used in real applications.
const fmt::BasicArgFormatter &af = fmt::Format("{0}"); const fmt::ArgFormatter &af = fmt::Format("{0}");
const_cast<fmt::BasicArgFormatter&>(af) << std::string("test"); const_cast<fmt::ArgFormatter&>(af) << std::string("test");
// String object passed as an argument to Print has been destroyed, // String object passed as an argument to Print has been destroyed,
// but BasicArgFormatter dtor hasn't been called yet. // but BasicArgFormatter dtor hasn't been called yet.
EXPECT_EQ("test", str(af)); 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; Formatter format;
format("Current point:\n"); format("Current point:\n");
format("({0:+f}, {1:+f})\n") << -3.14 << 3.14; 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