BasicArgFormatter -> ArgFormatter. FullFormat -> ActiveFormatter. Use ActiveFormatter to implement Format and Print.
This commit is contained in:
parent
57dbd2c3fe
commit
87b5ebfc4a
18
README.rst
18
README.rst
@ -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
|
||||
--------------------
|
||||
|
||||
|
@ -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
111
format.h
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user