ArgFormatter -> Formatter::ArgInserter. Test ArgInserter and ActiveFormatter.
This commit is contained in:
parent
1f6e4e9d7a
commit
e2725eeeb1
155
format.h
155
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 <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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user