Move formatting methods to BasicFormat.

This commit is contained in:
Victor Zverovich 2013-01-04 09:14:34 -08:00
parent 8e158d74cd
commit de17baae2d
3 changed files with 129 additions and 98 deletions

View File

@ -137,48 +137,6 @@ void FormatDecimal(char *buffer, uint64_t value, unsigned num_digits) {
buffer[0] = DIGITS[index]; buffer[0] = DIGITS[index];
} }
#ifdef _MSC_VER
int signbit(double value) {
if (value < 0) return 1;
if (value == value) return 0;
int dec = 0, sign = 0;
_ecvt(value, 0, &dec, &sign);
return sign;
}
#endif
}
void BasicFormatter::operator<<(int value) {
unsigned abs_value = value;
unsigned num_digits = 0;
char *out = 0;
if (value >= 0) {
num_digits = CountDigits(abs_value);
out = GrowBuffer(num_digits);
} else {
abs_value = 0 - abs_value;
num_digits = CountDigits(abs_value);
out = GrowBuffer(num_digits + 1);
*out++ = '-';
}
FormatDecimal(out, abs_value, num_digits);
}
// Throws Exception(message) if format contains '}', otherwise throws
// FormatError reporting unmatched '{'. The idea is that unmatched '{'
// should override other errors.
void Formatter::ReportError(const char *s, StringRef message) const {
for (int num_open_braces = num_open_braces_; *s; ++s) {
if (*s == '{') {
++num_open_braces;
} else if (*s == '}') {
if (--num_open_braces == 0)
throw fmt::FormatError(message);
}
}
throw fmt::FormatError("unmatched '{' in format");
}
// Fills the padding around the content and returns the pointer to the // Fills the padding around the content and returns the pointer to the
// content area. // content area.
char *FillPadding(char *buffer, char *FillPadding(char *buffer,
@ -192,7 +150,18 @@ char *FillPadding(char *buffer,
return content; return content;
} }
char *Formatter::PrepareFilledBuffer( #ifdef _MSC_VER
int signbit(double value) {
if (value < 0) return 1;
if (value == value) return 0;
int dec = 0, sign = 0;
_ecvt(value, 0, &dec, &sign);
return sign;
}
#endif
}
char *BasicFormatter::PrepareFilledBuffer(
unsigned size, const FormatSpec &spec, char sign) { unsigned size, const FormatSpec &spec, char sign) {
if (spec.width <= size) { if (spec.width <= size) {
char *p = GrowBuffer(size); char *p = GrowBuffer(size);
@ -225,7 +194,7 @@ char *Formatter::PrepareFilledBuffer(
} }
template <typename T> template <typename T>
void Formatter::FormatInt(T value, const FormatSpec &spec) { void BasicFormatter::FormatInt(T value, const FormatSpec &spec) {
unsigned size = 0; unsigned size = 0;
char sign = 0; char sign = 0;
typedef typename IntTraits<T>::UnsignedType UnsignedType; typedef typename IntTraits<T>::UnsignedType UnsignedType;
@ -289,7 +258,8 @@ void Formatter::FormatInt(T value, const FormatSpec &spec) {
} }
template <typename T> template <typename T>
void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) { void BasicFormatter::FormatDouble(
T value, const FormatSpec &spec, int precision) {
// Check type. // Check type.
char type = spec.type; char type = spec.type;
bool upper = false; bool upper = false;
@ -431,7 +401,7 @@ void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) {
} }
} }
char *Formatter::FormatString( char *BasicFormatter::FormatString(
const char *s, std::size_t size, const FormatSpec &spec) { const char *s, std::size_t size, const FormatSpec &spec) {
char *out = 0; char *out = 0;
if (spec.width > size) { if (spec.width > size) {
@ -451,6 +421,37 @@ char *Formatter::FormatString(
return out; return out;
} }
void BasicFormatter::operator<<(int value) {
unsigned abs_value = value;
unsigned num_digits = 0;
char *out = 0;
if (value >= 0) {
num_digits = CountDigits(abs_value);
out = GrowBuffer(num_digits);
} else {
abs_value = 0 - abs_value;
num_digits = CountDigits(abs_value);
out = GrowBuffer(num_digits + 1);
*out++ = '-';
}
FormatDecimal(out, abs_value, num_digits);
}
// Throws Exception(message) if format contains '}', otherwise throws
// FormatError reporting unmatched '{'. The idea is that unmatched '{'
// should override other errors.
void Formatter::ReportError(const char *s, StringRef message) const {
for (int num_open_braces = num_open_braces_; *s; ++s) {
if (*s == '{') {
++num_open_braces;
} else if (*s == '}') {
if (--num_open_braces == 0)
throw fmt::FormatError(message);
}
}
throw fmt::FormatError("unmatched '{' in format");
}
// Parses an unsigned integer advancing s to the end of the parsed input. // Parses an unsigned integer advancing s to the end of the parsed input.
// This function assumes that the first character of s is a digit. // This function assumes that the first character of s is a digit.
unsigned Formatter::ParseUInt(const char *&s) const { unsigned Formatter::ParseUInt(const char *&s) const {
@ -465,7 +466,7 @@ unsigned Formatter::ParseUInt(const char *&s) const {
return value; return value;
} }
const Formatter::Arg &Formatter::ParseArgIndex(const char *&s) { inline const Formatter::Arg &Formatter::ParseArgIndex(const char *&s) {
unsigned arg_index = 0; unsigned arg_index = 0;
if (*s < '0' || *s > '9') { if (*s < '0' || *s > '9') {
if (*s != '}' && *s != ':') if (*s != '}' && *s != ':')
@ -726,6 +727,5 @@ void Formatter::DoFormat() {
break; break;
} }
} }
buffer_.append(start, s + 1); buffer_.append(start, s);
buffer_.resize(buffer_.size() - 1); // Don't count the terminating zero.
} }

124
format.h
View File

@ -124,6 +124,10 @@ class ArgInserter;
type to allow passing different types of strings in a function, for example:: type to allow passing different types of strings in a function, for example::
TempFormatter<> Format(StringRef format); TempFormatter<> Format(StringRef format);
Format("{}") << 42;
Format(std::string("{}")) << 42;
Format(Format("{{}}")) << 42;
\endrst \endrst
*/ */
class StringRef { class StringRef {
@ -162,13 +166,14 @@ struct FormatSpec {
char type; char type;
char fill; char fill;
FormatSpec() : align(ALIGN_DEFAULT), flags(0), width(0), type(0), fill(' ') {} FormatSpec(unsigned width = 0, char type = 0, char fill = ' ')
: align(ALIGN_DEFAULT), flags(0), width(width), type(type), fill(fill) {}
}; };
class BasicFormatter { class BasicFormatter {
protected: protected:
enum { INLINE_BUFFER_SIZE = 500 }; enum { INLINE_BUFFER_SIZE = 500 };
internal::Array<char, INLINE_BUFFER_SIZE> buffer_; // Output buffer. mutable internal::Array<char, INLINE_BUFFER_SIZE> buffer_; // Output buffer.
// Grows the buffer by n characters and returns a pointer to the newly // Grows the buffer by n characters and returns a pointer to the newly
// allocated area. // allocated area.
@ -178,8 +183,69 @@ class BasicFormatter {
return &buffer_[size]; return &buffer_[size];
} }
char *PrepareFilledBuffer(unsigned size, const FormatSpec &spec, char sign);
// Formats an integer.
template <typename T>
void FormatInt(T value, const FormatSpec &spec);
// Formats a floating point number (double or long double).
template <typename T>
void FormatDouble(T value, const FormatSpec &spec, int precision);
char *FormatString(const char *s, std::size_t size, const FormatSpec &spec);
public: public:
/**
\rst
Returns the number of characters written to the output buffer.
\endrst
*/
std::size_t size() const { return buffer_.size(); }
/**
\rst
Returns a pointer to the output buffer content. No terminating null
character is appended.
\endrst
*/
const char *data() const { return &buffer_[0]; }
/**
\rst
Returns a pointer to the output buffer content with terminating null
character appended.
\endrst
*/
const char *c_str() const {
std::size_t size = buffer_.size();
buffer_.reserve(size + 1);
buffer_[size] = '\0';
return &buffer_[0];
}
/**
\rst
Returns the content of the output buffer as an ``std::string``.
\endrst
*/
std::string str() const { return std::string(&buffer_[0], buffer_.size()); }
void operator<<(int value); void operator<<(int value);
void operator<<(char value) {
*GrowBuffer(1) = value;
}
void operator<<(const char *value) {
std::size_t size = std::strlen(value);
std::strncpy(GrowBuffer(size), value, size);
}
BasicFormatter &Write(int value, const FormatSpec &spec) {
FormatInt(value, spec);
return *this;
}
}; };
/** /**
@ -324,18 +390,6 @@ class Formatter : public BasicFormatter {
void ReportError(const char *s, StringRef message) const; void ReportError(const char *s, StringRef message) const;
char *PrepareFilledBuffer(unsigned size, const FormatSpec &spec, char sign);
// Formats an integer.
template <typename T>
void FormatInt(T value, const FormatSpec &spec);
// Formats a floating point number (double or long double).
template <typename T>
void FormatDouble(T value, const FormatSpec &spec, int precision);
char *FormatString(const char *s, std::size_t size, const FormatSpec &spec);
// 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, const FormatSpec &spec); void FormatCustomArg(const void *arg, const FormatSpec &spec);
@ -360,7 +414,7 @@ class Formatter : public BasicFormatter {
Constructs a formatter with an empty output buffer. Constructs a formatter with an empty output buffer.
\endrst \endrst
*/ */
Formatter() : format_(0) { buffer_[0] = 0; } Formatter() : format_(0) {}
/** /**
\rst \rst
@ -370,36 +424,6 @@ class Formatter : public BasicFormatter {
\endrst \endrst
*/ */
internal::ArgInserter operator()(StringRef format); internal::ArgInserter operator()(StringRef format);
/**
\rst
Returns the number of characters written to the output buffer.
\endrst
*/
std::size_t size() const { return buffer_.size(); }
/**
\rst
Returns a pointer to the output buffer content. No terminating null
character is appended.
\endrst
*/
const char *data() const { return &buffer_[0]; }
/**
\rst
Returns a pointer to the output buffer content with terminating null
character appended.
\endrst
*/
const char *c_str() const { return &buffer_[0]; }
/**
\rst
Returns the content of the output buffer as an ``std::string``.
\endrst
*/
std::string str() const { return std::string(&buffer_[0], buffer_.size()); }
}; };
namespace internal { namespace internal {
@ -540,9 +564,13 @@ struct NoAction {
void operator()(const Formatter &) const {} void operator()(const Formatter &) const {}
}; };
// A formatter with an action performed when formatting is complete. /**
// Objects of this class normally exist only as temporaries returned \rst
// by one of the formatting functions, thus the name. A formatter with an action performed when formatting is complete.
Objects of this class normally exist only as temporaries returned
by one of the formatting functions which explains the name.
\endrst
*/
template <typename Action = NoAction> template <typename Action = NoAction>
class TempFormatter : public internal::ArgInserter { class TempFormatter : public internal::ArgInserter {
private: private:

View File

@ -898,7 +898,6 @@ TEST(FormatterTest, FormatStringFromSpeedTest) {
TEST(FormatterTest, FormatterCtor) { TEST(FormatterTest, FormatterCtor) {
Formatter format; Formatter format;
EXPECT_EQ(0u, format.size()); EXPECT_EQ(0u, format.size());
EXPECT_STREQ("", format.data());
EXPECT_STREQ("", format.c_str()); EXPECT_STREQ("", format.c_str());
EXPECT_EQ("", format.str()); EXPECT_EQ("", format.str());
format("part{0}") << 1; format("part{0}") << 1;
@ -921,6 +920,10 @@ TEST(FormatterTest, FormatterAppend) {
} }
TEST(FormatterTest, FormatterExamples) { TEST(FormatterTest, FormatterExamples) {
EXPECT_EQ("42", str(Format("{}") << 42));
EXPECT_EQ("42", str(Format(std::string("{}")) << 42));
EXPECT_EQ("42", str(Format(Format("{{}}")) << 42));
Formatter format; Formatter format;
format("Current point:\n"); format("Current point:\n");
format("({0:+f}, {1:+f})\n") << -3.14 << 3.14; format("({0:+f}, {1:+f})\n") << -3.14 << 3.14;