forked from fmtlib/fmt
ArgFormatter -> Formatter::ArgInserter. Test ArgInserter and ActiveFormatter.
This commit is contained in:
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) {}
|
FormatError(const std::string &message) : std::runtime_error(message) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
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.
|
||||||
// Usage:
|
// Usage:
|
||||||
@@ -193,7 +191,7 @@ class Formatter {
|
|||||||
// 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 where 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 ArgFormatter is called.
|
// the destructor of ArgInserter is called.
|
||||||
formatter->Format();
|
formatter->Format();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -203,7 +201,65 @@ class Formatter {
|
|||||||
|
|
||||||
const char *format_; // Format string.
|
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) {
|
void Add(const Arg &arg) {
|
||||||
args_.push_back(&arg);
|
args_.push_back(&arg);
|
||||||
@@ -241,9 +297,9 @@ class Formatter {
|
|||||||
Formatter() : format_(0) { buffer_[0] = 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 ArgFormatter object
|
// Arguments are accepted through the returned ArgInserter object
|
||||||
// using inserter operator<<.
|
// using inserter operator<<.
|
||||||
ArgFormatter operator()(const char *format);
|
ArgInserter operator()(const char *format);
|
||||||
|
|
||||||
std::size_t size() const { return buffer_.size(); }
|
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()); }
|
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>
|
template <typename T>
|
||||||
void Formatter::FormatCustomArg(const void *arg, int width) {
|
void Formatter::FormatCustomArg(const void *arg, int width) {
|
||||||
const T &value = *static_cast<const T*>(arg);
|
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(), ' ');
|
std::fill_n(out + str.size(), width - str.size(), ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ArgFormatter Formatter::operator()(const char *format) {
|
inline Formatter::ArgInserter Formatter::operator()(const char *format) {
|
||||||
ArgFormatter formatter(*this);
|
ArgInserter formatter(this);
|
||||||
format_ = format;
|
format_ = format;
|
||||||
args_.clear();
|
args_.clear();
|
||||||
return formatter;
|
return formatter;
|
||||||
@@ -327,40 +330,54 @@ inline ArgFormatter Formatter::operator()(const char *format) {
|
|||||||
|
|
||||||
// A formatter with an action performed when formatting is complete.
|
// A formatter with an action performed when formatting is complete.
|
||||||
template <typename Action>
|
template <typename Action>
|
||||||
class ActiveFormatter : public ArgFormatter {
|
class ActiveFormatter : public Formatter::ArgInserter {
|
||||||
private:
|
private:
|
||||||
mutable Formatter formatter_;
|
Formatter formatter_;
|
||||||
|
Action action_;
|
||||||
|
|
||||||
// Do not implement.
|
// Do not implement.
|
||||||
ActiveFormatter& operator=(const ActiveFormatter&);
|
ActiveFormatter& operator=(const ActiveFormatter&);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ActiveFormatter(const char *format) : ArgFormatter(formatter_) {
|
// Creates an active formatter with a format string and an action.
|
||||||
ArgFormatter::operator=(formatter_(format));
|
// 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() {
|
~ActiveFormatter() {
|
||||||
Action()(*FinishFormatting());
|
action_(*Format());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A formatting action that does nothing.
|
||||||
struct Ignore {
|
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) {
|
inline ActiveFormatter<Ignore> Format(const char *format) {
|
||||||
ActiveFormatter<Ignore> af(format);
|
ActiveFormatter<Ignore> af(format);
|
||||||
return af;
|
return af;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A formatting action that writes formatted output to stdout.
|
||||||
struct Write {
|
struct Write {
|
||||||
void operator()(Formatter &f) const {
|
void operator()(const Formatter &f) const {
|
||||||
std::fwrite(f.data(), 1, f.size(), stdout);
|
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) {
|
inline ActiveFormatter<Write> Print(const char *format) {
|
||||||
ActiveFormatter<Write> af(format);
|
ActiveFormatter<Write> af(format);
|
||||||
return af;
|
return af;
|
||||||
|
@@ -522,16 +522,6 @@ TEST(FormatterTest, FormatStringFromSpeedTest) {
|
|||||||
<< reinterpret_cast<void*>(1000) << 'X'));
|
<< 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) {
|
TEST(FormatterTest, FormatterCtor) {
|
||||||
Formatter format;
|
Formatter format;
|
||||||
EXPECT_EQ(0, format.size());
|
EXPECT_EQ(0, format.size());
|
||||||
@@ -564,9 +554,44 @@ TEST(FormatterTest, FormatterExample) {
|
|||||||
EXPECT_EQ("Current point:\n(-3.140000, +3.140000)\n", format.str());
|
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 {
|
struct PrintError {
|
||||||
void operator()(const fmt::Formatter &f) const {
|
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;
|
return af;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, ArgFormatter) {
|
TEST(ActiveFormatterTest, Example) {
|
||||||
std::string path = "somefile";
|
std::string path = "somefile";
|
||||||
ReportError("File not found: {0}") << path;
|
ReportError("File not found: {0}") << path;
|
||||||
}
|
}
|
||||||
// TODO: test ArgFormatter
|
|
||||||
|
Reference in New Issue
Block a user