diff --git a/format.cc b/format.cc index 568c8ce8..6aca6e6c 100644 --- a/format.cc +++ b/format.cc @@ -137,48 +137,6 @@ void FormatDecimal(char *buffer, uint64_t value, unsigned num_digits) { 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 // content area. char *FillPadding(char *buffer, @@ -192,7 +150,18 @@ char *FillPadding(char *buffer, 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) { if (spec.width <= size) { char *p = GrowBuffer(size); @@ -225,7 +194,7 @@ char *Formatter::PrepareFilledBuffer( } template -void Formatter::FormatInt(T value, const FormatSpec &spec) { +void BasicFormatter::FormatInt(T value, const FormatSpec &spec) { unsigned size = 0; char sign = 0; typedef typename IntTraits::UnsignedType UnsignedType; @@ -289,7 +258,8 @@ void Formatter::FormatInt(T value, const FormatSpec &spec) { } template -void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) { +void BasicFormatter::FormatDouble( + T value, const FormatSpec &spec, int precision) { // Check type. char type = spec.type; 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) { char *out = 0; if (spec.width > size) { @@ -451,6 +421,37 @@ char *Formatter::FormatString( 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. // This function assumes that the first character of s is a digit. unsigned Formatter::ParseUInt(const char *&s) const { @@ -465,7 +466,7 @@ unsigned Formatter::ParseUInt(const char *&s) const { return value; } -const Formatter::Arg &Formatter::ParseArgIndex(const char *&s) { +inline const Formatter::Arg &Formatter::ParseArgIndex(const char *&s) { unsigned arg_index = 0; if (*s < '0' || *s > '9') { if (*s != '}' && *s != ':') @@ -726,6 +727,5 @@ void Formatter::DoFormat() { break; } } - buffer_.append(start, s + 1); - buffer_.resize(buffer_.size() - 1); // Don't count the terminating zero. + buffer_.append(start, s); } diff --git a/format.h b/format.h index 4e9cf741..fcf456b9 100644 --- a/format.h +++ b/format.h @@ -124,6 +124,10 @@ class ArgInserter; type to allow passing different types of strings in a function, for example:: TempFormatter<> Format(StringRef format); + + Format("{}") << 42; + Format(std::string("{}")) << 42; + Format(Format("{{}}")) << 42; \endrst */ class StringRef { @@ -162,13 +166,14 @@ struct FormatSpec { char type; 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 { protected: enum { INLINE_BUFFER_SIZE = 500 }; - internal::Array buffer_; // Output buffer. + mutable internal::Array buffer_; // Output buffer. // Grows the buffer by n characters and returns a pointer to the newly // allocated area. @@ -178,8 +183,69 @@ class BasicFormatter { return &buffer_[size]; } + char *PrepareFilledBuffer(unsigned size, const FormatSpec &spec, char sign); + + // Formats an integer. + template + void FormatInt(T value, const FormatSpec &spec); + + // Formats a floating point number (double or long double). + template + void FormatDouble(T value, const FormatSpec &spec, int precision); + + char *FormatString(const char *s, std::size_t size, const FormatSpec &spec); + 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<<(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; - char *PrepareFilledBuffer(unsigned size, const FormatSpec &spec, char sign); - - // Formats an integer. - template - void FormatInt(T value, const FormatSpec &spec); - - // Formats a floating point number (double or long double). - template - 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. template void FormatCustomArg(const void *arg, const FormatSpec &spec); @@ -360,7 +414,7 @@ class Formatter : public BasicFormatter { Constructs a formatter with an empty output buffer. \endrst */ - Formatter() : format_(0) { buffer_[0] = 0; } + Formatter() : format_(0) {} /** \rst @@ -370,36 +424,6 @@ class Formatter : public BasicFormatter { \endrst */ 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 { @@ -540,9 +564,13 @@ struct NoAction { 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 -// by one of the formatting functions, thus the name. +/** + \rst + 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 class TempFormatter : public internal::ArgInserter { private: diff --git a/format_test.cc b/format_test.cc index 890dc9de..8f7a0db6 100644 --- a/format_test.cc +++ b/format_test.cc @@ -898,7 +898,6 @@ TEST(FormatterTest, FormatStringFromSpeedTest) { TEST(FormatterTest, FormatterCtor) { Formatter format; EXPECT_EQ(0u, format.size()); - EXPECT_STREQ("", format.data()); EXPECT_STREQ("", format.c_str()); EXPECT_EQ("", format.str()); format("part{0}") << 1; @@ -921,6 +920,10 @@ TEST(FormatterTest, FormatterAppend) { } 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; format("Current point:\n"); format("({0:+f}, {1:+f})\n") << -3.14 << 3.14;