diff --git a/format.cc b/format.cc index 1a062b39..2c538c41 100644 --- a/format.cc +++ b/format.cc @@ -1,5 +1,5 @@ /* - Small, safe and fast printf-like formatting library for C++ + Small, safe and fast string formatting library for C++ Author: Victor Zverovich */ @@ -363,6 +363,7 @@ void fmt::Formatter::Format() { } } buffer_.append(start, s + 1); + buffer_.resize(buffer_.size() - 1); // Don't count the terminating zero. } fmt::ArgFormatter::~ArgFormatter() { diff --git a/format.h b/format.h index f1150207..076b24d2 100644 --- a/format.h +++ b/format.h @@ -1,5 +1,5 @@ /* - Small, safe and fast printf-like formatting library for C++ + Small, safe and fast string formatting library for C++ Author: Victor Zverovich */ @@ -46,9 +46,13 @@ class Buffer { if (ptr_ != data_) delete [] ptr_; } + // Returns the size of this buffer. std::size_t size() const { return size_; } + + // Returns the capacity of this buffer. std::size_t capacity() const { return capacity_; } + // Resizes the buffer. If T is a POD type new elements are not initialized. void resize(std::size_t new_size) { if (new_size > capacity_) Grow(new_size); @@ -60,18 +64,19 @@ class Buffer { Grow(capacity); } + void clear() { size_ = 0; } + void push_back(const T &value) { if (size_ == capacity_) Grow(size_ + 1); ptr_[size_++] = value; } + // Appends data to the end of the buffer. void append(const T *begin, const T *end); T &operator[](std::size_t index) { return ptr_[index]; } const T &operator[](std::size_t index) const { return ptr_[index]; } - - void clear() { size_ = 0; } }; template @@ -93,11 +98,24 @@ void Buffer::append(const T *begin, const T *end) { size_ += num_elements; } -// A sprintf-like formatter that automatically allocates enough storage to -// fit all the output. +// Formatter provides string formatting functionality similar to Python's +// str.format. The output is stored in a memory buffer that grows dynamically. +// Usage: +// +// Formatter out; +// out("Current point:\n"); +// out("(-{:+f}, {:+f})") << 3.14 << -3.14; +// +// This will populate the buffer of the out object with the following output: +// +// Current point: +// (-3.140000, +3.140000) +// +// The buffer can be accessed using Formatter::data() or Formatter::c_str(). class Formatter { private: - Buffer buffer_; // Output buffer. + enum { INLINE_BUFFER_SIZE = 500 }; + Buffer buffer_; // Output buffer. enum Type { // Numeric types should go first. @@ -108,7 +126,6 @@ class Formatter { typedef void (Formatter::*FormatFunc)(const void *arg, int width); - public: // A format argument. struct Arg { Type type; @@ -146,7 +163,8 @@ class Formatter { : type(CUSTOM), custom_value(value), format(f) {} }; - Buffer args_; // Format arguments. + enum { NUM_INLINE_ARGS = 10 }; + Buffer args_; // Format arguments. const char *format_; // Format string. @@ -297,9 +315,6 @@ class ArgFormatter { return *this << const_value; } - // If T is a pointer type, say "U*", AddPtrConst::Value will be - // "const U*". This additional const ensures that operator<<(const void *) - // and not this method is called both for "const void*" and "void*". template ArgFormatter &operator<<(const T &value) { formatter_->Add(Formatter::Arg(&value, &Formatter::FormatCustomArg)); diff --git a/format_test.cc b/format_test.cc index e6c7c611..d36ab42d 100644 --- a/format_test.cc +++ b/format_test.cc @@ -445,3 +445,12 @@ TEST(FormatterTest, FormatStringFromSpeedTest) { << 1.234 << 42 << 3.13 << "str" << reinterpret_cast(1000) << 'X')); } + +TEST(FormatterTest, Formatter) { + Formatter format; + format("Current point:\n"); + format("({0:+f}, {1:+f})\n") << -3.14 << 3.14; + EXPECT_STREQ("Current point:\n(-3.140000, +3.140000)\n", format.c_str()); +} + +// TODO: test Buffer