mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-31 11:17:35 +02:00
Preliminary support for custom formatting.
This commit is contained in:
@ -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(), ' ');
|
||||||
|
}
|
||||||
|
51
format.h
51
format.h
@ -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 {
|
|||||||
// 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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
// 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 {
|
|||||||
|
|
||||||
void DoFormat();
|
void DoFormat();
|
||||||
|
|
||||||
void Format() {
|
void CompleteFormatting() {
|
||||||
if (!format_) return;
|
if (!format_) return;
|
||||||
DoFormat();
|
DoFormat();
|
||||||
}
|
}
|
||||||
@ -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 {
|
|||||||
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 {
|
|||||||
};
|
};
|
||||||
|
|
||||||
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 {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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) {
|
|||||||
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}:%")
|
||||||
|
Reference in New Issue
Block a user