Commit ade5381f authored by Victor Zverovich's avatar Victor Zverovich

Preliminary support for custom formatting.

parent 059934fd
...@@ -433,3 +433,10 @@ void Formatter::DoFormat() { ...@@ -433,3 +433,10 @@ void 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.
} }
void Formatter::Write(const std::string &s, unsigned width) {
char *out = GrowBuffer(std::max<std::size_t>(width, s.size()));
std::copy(s.begin(), s.end(), out);
if (width > s.size())
std::fill_n(out + s.size(), width - s.size(), ' ');
}
...@@ -148,7 +148,7 @@ class Formatter { ...@@ -148,7 +148,7 @@ class Formatter {
CHAR, STRING, WSTRING, POINTER, CUSTOM 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. // A format argument.
class Arg { class Arg {
...@@ -236,7 +236,7 @@ class Formatter { ...@@ -236,7 +236,7 @@ class Formatter {
// 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 ArgInserter is called. // the destructor of ArgInserter is called.
formatter->Format(); formatter->CompleteFormatting();
} }
}; };
...@@ -247,6 +247,7 @@ class Formatter { ...@@ -247,6 +247,7 @@ class Formatter {
int num_open_braces_; int num_open_braces_;
friend class internal::ArgInserter; friend class internal::ArgInserter;
friend class ArgFormatter;
void Add(const Arg &arg) { void Add(const Arg &arg) {
args_.push_back(&arg); args_.push_back(&arg);
...@@ -265,7 +266,7 @@ class Formatter { ...@@ -265,7 +266,7 @@ class Formatter {
// Formats an argument of a custom type, such as a user-defined class. // Formats an argument of a custom type, such as a user-defined class.
template <typename T> template <typename T>
void FormatCustomArg(const void *arg, int width); void FormatCustomArg(const void *arg, unsigned width);
unsigned ParseUInt(const char *&s) const; unsigned ParseUInt(const char *&s) const;
...@@ -274,7 +275,7 @@ class Formatter { ...@@ -274,7 +275,7 @@ class Formatter {
void DoFormat(); void DoFormat();
void Format() { void CompleteFormatting() {
if (!format_) return; if (!format_) return;
DoFormat(); DoFormat();
} }
...@@ -301,6 +302,10 @@ class Formatter { ...@@ -301,6 +302,10 @@ class Formatter {
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()); } 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 { namespace internal {
...@@ -336,7 +341,7 @@ class ArgInserter { ...@@ -336,7 +341,7 @@ class ArgInserter {
Formatter *f = formatter_; Formatter *f = formatter_;
if (f) { if (f) {
formatter_ = 0; formatter_ = 0;
f->Format(); f->CompleteFormatting();
} }
return f; return f;
} }
...@@ -352,14 +357,14 @@ class ArgInserter { ...@@ -352,14 +357,14 @@ class ArgInserter {
}; };
static Formatter *Format(Proxy p) { static Formatter *Format(Proxy p) {
p.formatter->Format(); p.formatter->CompleteFormatting();
return p.formatter; return p.formatter;
} }
public: public:
~ArgInserter() { ~ArgInserter() {
if (formatter_) if (formatter_)
formatter_->Format(); formatter_->CompleteFormatting();
} }
// Feeds an argument to a formatter. // Feeds an argument to a formatter.
...@@ -387,16 +392,34 @@ class ArgInserter { ...@@ -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 <typename T> template <typename T>
void Formatter::FormatCustomArg(const void *arg, int width) { void Format(ArgFormatter &af, unsigned width, const T &value) {
const T &value = *static_cast<const T*>(arg);
std::ostringstream os; std::ostringstream os;
os << value; os << value;
std::string str(os.str()); af.Write(os.str(), width);
char *out = GrowBuffer(std::max<std::size_t>(width, str.size())); }
std::copy(str.begin(), str.end(), out);
if (static_cast<unsigned>(width) > str.size()) template <typename T>
std::fill_n(out + str.size(), width - str.size(), ' '); void Formatter::FormatCustomArg(const void *arg, unsigned width) {
ArgFormatter af(*this);
Format(af, width, *static_cast<const T*>(arg));
} }
inline internal::ArgInserter Formatter::operator()(const char *format) { inline internal::ArgInserter Formatter::operator()(const char *format) {
......
...@@ -621,6 +621,27 @@ TEST(FormatterTest, FormatString) { ...@@ -621,6 +621,27 @@ TEST(FormatterTest, FormatString) {
EXPECT_EQ("test", str(Format("{0}") << std::string("test"))); 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 { class Date {
int year_, month_, day_; int year_, month_, day_;
public: public:
...@@ -632,7 +653,7 @@ class Date { ...@@ -632,7 +653,7 @@ class Date {
} }
}; };
TEST(FormatterTest, FormatCustom) { TEST(FormatterTest, FormatUsingIOStreams) {
EXPECT_EQ("a string", str(Format("{0}") << TestString("a string"))); EXPECT_EQ("a string", str(Format("{0}") << TestString("a string")));
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));
EXPECT_EQ("The date is 2012-12-9", s); EXPECT_EQ("The date is 2012-12-9", s);
...@@ -640,6 +661,16 @@ TEST(FormatterTest, FormatCustom) { ...@@ -640,6 +661,16 @@ TEST(FormatterTest, FormatCustom) {
CheckUnknownTypes(date, "", "object"); 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) { TEST(FormatterTest, FormatStringFromSpeedTest) {
EXPECT_EQ("1.2340000000:0042:+3.13:str:0x3e8:X:%", EXPECT_EQ("1.2340000000:0042:+3.13:str:0x3e8:X:%",
str(Format("{0:0.10f}:{1:04}:{2:+g}:{3}:{4}:{5}:%") str(Format("{0:0.10f}:{1:04}:{2:+g}:{3}:{4}:{5}:%")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment