From ade5381f9ac1c89dab356fabbb7b2a8a67e2542b Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 17 Dec 2012 14:56:44 -0800 Subject: [PATCH] Preliminary support for custom formatting. --- format.cc | 7 +++++++ format.h | 51 ++++++++++++++++++++++++++++++++++++-------------- format_test.cc | 33 +++++++++++++++++++++++++++++++- 3 files changed, 76 insertions(+), 15 deletions(-) diff --git a/format.cc b/format.cc index 27866f25..16734aef 100644 --- a/format.cc +++ b/format.cc @@ -433,3 +433,10 @@ void Formatter::DoFormat() { buffer_.append(start, s + 1); buffer_.resize(buffer_.size() - 1); // Don't count the terminating zero. } + +void Formatter::Write(const std::string &s, unsigned width) { + char *out = GrowBuffer(std::max(width, s.size())); + std::copy(s.begin(), s.end(), out); + if (width > s.size()) + std::fill_n(out + s.size(), width - s.size(), ' '); +} diff --git a/format.h b/format.h index c9c8832b..e6e892ad 100644 --- a/format.h +++ b/format.h @@ -148,7 +148,7 @@ class Formatter { CHAR, STRING, WSTRING, POINTER, CUSTOM }; - typedef void (Formatter::*FormatFunc)(const void *arg, int width); + typedef void (Formatter::*FormatFunc)(const void *arg, unsigned width); // A format argument. class Arg { @@ -236,7 +236,7 @@ class Formatter { // 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 ArgInserter is called. - formatter->Format(); + formatter->CompleteFormatting(); } }; @@ -247,6 +247,7 @@ class Formatter { int num_open_braces_; friend class internal::ArgInserter; + friend class ArgFormatter; void Add(const Arg &arg) { args_.push_back(&arg); @@ -265,7 +266,7 @@ class Formatter { // Formats an argument of a custom type, such as a user-defined class. template - void FormatCustomArg(const void *arg, int width); + void FormatCustomArg(const void *arg, unsigned width); unsigned ParseUInt(const char *&s) const; @@ -274,7 +275,7 @@ class Formatter { void DoFormat(); - void Format() { + void CompleteFormatting() { if (!format_) return; DoFormat(); } @@ -301,6 +302,10 @@ class Formatter { const char *c_str() const { return &buffer_[0]; } std::string str() const { return std::string(&buffer_[0], buffer_.size()); } + + // Writes a string to the output buffer padding with spaces if + // necessary to achieve the desired width. + void Write(const std::string &s, unsigned width); }; namespace internal { @@ -336,7 +341,7 @@ class ArgInserter { Formatter *f = formatter_; if (f) { formatter_ = 0; - f->Format(); + f->CompleteFormatting(); } return f; } @@ -352,14 +357,14 @@ class ArgInserter { }; static Formatter *Format(Proxy p) { - p.formatter->Format(); + p.formatter->CompleteFormatting(); return p.formatter; } public: ~ArgInserter() { if (formatter_) - formatter_->Format(); + formatter_->CompleteFormatting(); } // Feeds an argument to a formatter. @@ -387,16 +392,34 @@ class ArgInserter { }; } +// ArgFormatter provides access to the format buffer within custom +// Format functions. It is not desirable to pass Formatter to these +// functions because Formatter::operator() is not reentrant and +// therefore can't be used for argument formatting. +class ArgFormatter { + private: + Formatter &formatter_; + + public: + explicit ArgFormatter(Formatter &f) : formatter_(f) {} + + void Write(const std::string &s, unsigned width) { + formatter_.Write(s, width); + } +}; + +// The default formatting function. template -void Formatter::FormatCustomArg(const void *arg, int width) { - const T &value = *static_cast(arg); +void Format(ArgFormatter &af, unsigned width, const T &value) { std::ostringstream os; os << value; - std::string str(os.str()); - char *out = GrowBuffer(std::max(width, str.size())); - std::copy(str.begin(), str.end(), out); - if (static_cast(width) > str.size()) - std::fill_n(out + str.size(), width - str.size(), ' '); + af.Write(os.str(), width); +} + +template +void Formatter::FormatCustomArg(const void *arg, unsigned width) { + ArgFormatter af(*this); + Format(af, width, *static_cast(arg)); } inline internal::ArgInserter Formatter::operator()(const char *format) { diff --git a/format_test.cc b/format_test.cc index ea6dd21e..2dbf00cb 100644 --- a/format_test.cc +++ b/format_test.cc @@ -621,6 +621,27 @@ TEST(FormatterTest, FormatString) { EXPECT_EQ("test", str(Format("{0}") << std::string("test"))); } +TEST(FormatterTest, Write) { + Formatter format; + format.Write("12", 2); + EXPECT_EQ("12", format.str()); + format.Write("34", 4); + EXPECT_EQ("1234 ", format.str()); + format.Write("56", 0); + EXPECT_EQ("1234 56", format.str()); +} + +TEST(ArgFormatterTest, Write) { + Formatter formatter; + fmt::ArgFormatter format(formatter); + format.Write("12", 2); + EXPECT_EQ("12", formatter.str()); + format.Write("34", 4); + EXPECT_EQ("1234 ", formatter.str()); + format.Write("56", 0); + EXPECT_EQ("1234 56", formatter.str()); +} + class Date { int year_, month_, day_; public: @@ -632,7 +653,7 @@ class Date { } }; -TEST(FormatterTest, FormatCustom) { +TEST(FormatterTest, FormatUsingIOStreams) { EXPECT_EQ("a string", str(Format("{0}") << TestString("a string"))); std::string s = str(fmt::Format("The date is {0}") << Date(2012, 12, 9)); EXPECT_EQ("The date is 2012-12-9", s); @@ -640,6 +661,16 @@ TEST(FormatterTest, FormatCustom) { CheckUnknownTypes(date, "", "object"); } +class Answer {}; + +void Format(fmt::ArgFormatter &af, unsigned width, Answer) { + af.Write("42", width); +} + +TEST(FormatterTest, CustomFormat) { + EXPECT_EQ("42", str(Format("{0}") << Answer())); +} + TEST(FormatterTest, FormatStringFromSpeedTest) { EXPECT_EQ("1.2340000000:0042:+3.13:str:0x3e8:X:%", str(Format("{0:0.10f}:{1:04}:{2:+g}:{3}:{4}:{5}:%")