diff --git a/CMakeLists.txt b/CMakeLists.txt index d0300120..59edd7d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,7 +83,7 @@ endfunction() # Define the fmt library, its includes and the needed defines. # format.cc is added to FMT_HEADERS for the header-only configuration. add_headers(FMT_HEADERS core.h format.h format.cc locale.h ostream.h printf.h - string.h time.h) + time.h) if (HAVE_OPEN) add_headers(FMT_HEADERS posix.h) add_headers(FMT_SOURCES posix.cc) diff --git a/include/fmt/core.h b/include/fmt/core.h index a8a33c10..350ce0fa 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -176,6 +176,18 @@ class basic_string_view { using string_view = basic_string_view; using wstring_view = basic_string_view; +template +class basic_arg; + +template +class basic_format_args; + +// A formatter for objects of type T. +template +struct formatter; + +namespace internal { + /** A contiguous memory buffer with an optional growing ability. */ template class basic_buffer { @@ -253,21 +265,6 @@ class basic_buffer { const T &operator[](std::size_t index) const { return ptr_[index]; } }; -using buffer = basic_buffer; -using wbuffer = basic_buffer; - -template -class basic_arg; - -template -class basic_format_args; - -// A formatter for objects of type T. -template -struct formatter; - -namespace internal { - // A helper function to suppress bogus "conditional expression is constant" // warnings. template @@ -583,7 +580,7 @@ class value { typename Context::template formatter_type f; auto &&parse_ctx = ctx.parse_context(); parse_ctx.advance_to(f.parse(parse_ctx)); - ctx.advance_to(f.format(*static_cast(arg), ctx)); + f.format(*static_cast(arg), ctx); } }; @@ -797,7 +794,7 @@ class context_base { Range range() { return range_; } // Returns an iterator to the beginning of the output range. - iterator begin() { return out_; } + auto begin() { return std::back_inserter(range_.container()); } // Advances the begin iterator to ``it``. void advance_to(iterator it) { out_ = it; } @@ -878,6 +875,9 @@ class basic_context : format_arg get_arg(basic_string_view name); }; +using buffer = internal::basic_buffer; +using wbuffer = internal::basic_buffer; + using context = basic_context>; using wcontext = basic_context>; diff --git a/include/fmt/format.h b/include/fmt/format.h index 47149cde..fe60d257 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -296,17 +297,6 @@ inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { template inline T *make_ptr(T *ptr, std::size_t) { return ptr; } #endif -} // namespace internal - -// A wrapper around std::locale used to reduce compile times since -// is very heavy. -class locale; - -class locale_provider { - public: - virtual ~locale_provider() {} - virtual fmt::locale locale(); -}; template template @@ -318,10 +308,32 @@ void basic_buffer::append(const U *begin, const U *end) { size_ = new_size; } -template -inline std::basic_string to_string(const basic_buffer& buffer) { - return std::basic_string(buffer.data(), buffer.size()); -} +template +class container_buffer + : public internal::basic_buffer { + private: + Container &container_; + + protected: + virtual void grow(std::size_t capacity) { + container_.resize(capacity); + this->set(&container_[0], capacity); + } + + public: + explicit container_buffer(Container &c) : container_(c) {} +}; +} // namespace internal + +// A wrapper around std::locale used to reduce compile times since +// is very heavy. +class locale; + +class locale_provider { + public: + virtual ~locale_provider() {} + virtual fmt::locale locale(); +}; /** \rst @@ -354,7 +366,7 @@ inline std::basic_string to_string(const basic_buffer& buffer) { */ template > -class basic_memory_buffer : private Allocator, public basic_buffer { +class basic_memory_buffer: private Allocator, public internal::basic_buffer { private: T store_[SIZE]; @@ -453,7 +465,7 @@ typedef basic_memory_buffer wmemory_buffer; \endrst */ template -class basic_fixed_buffer : public basic_buffer { +class basic_fixed_buffer : public internal::basic_buffer { public: /** \rst @@ -2790,7 +2802,7 @@ struct formatter< } template - typename FormatContext::iterator format(const T &val, FormatContext &ctx) { + auto format(const T &val, FormatContext &ctx) -> decltype(ctx.begin()) { internal::handle_dynamic_spec( specs_.width_, specs_.width_ref, ctx); internal::handle_dynamic_spec( @@ -2893,9 +2905,9 @@ typename basic_context::format_arg /** Formats arguments and writes the output to the buffer. */ template -void vformat_to(typename ArgFormatter::range out, - basic_string_view format_str, - basic_format_args args) { +void do_vformat_to(typename ArgFormatter::range out, + basic_string_view format_str, + basic_format_args args) { using iterator = internal::null_terminating_iterator; using range = typename ArgFormatter::range; @@ -2983,32 +2995,64 @@ constexpr fill_spec_factory fill; constexpr format_spec_factory width; constexpr format_spec_factory type; -template -inline void vformat_range(Range out, string_view format_str, format_args args) { - vformat_to>(out, format_str, args); +/** + \rst + Converts *value* to ``std::string`` using the default format for type *T*. + + **Example**:: + + #include "fmt/string.h" + + std::string answer = fmt::to_string(42); + \endrst + */ +template +std::string to_string(const T &value) { + std::string str; + internal::container_buffer buf(str); + writer(buf).write(value); + return str; } -template -inline void format_range(Range out, string_view format_str, - const Args & ... args) { - vformat_range(out, format_str, make_args(args...)); +template +std::basic_string to_string(const basic_memory_buffer &buffer) { + return std::basic_string(buffer.data(), buffer.size()); } inline void vformat_to(buffer &buf, string_view format_str, format_args args) { using range = internal::dynamic_range; - vformat_to>(buf, format_str, args); + do_vformat_to>(buf, format_str, args); } inline void vformat_to(wbuffer &buf, wstring_view format_str, wformat_args args) { using range = internal::dynamic_range; - vformat_to>(buf, format_str, args); + do_vformat_to>(buf, format_str, args); +} + +template +void vformat_to(std::back_insert_iterator out, + string_view format_str, format_args args) { + using iterator = std::back_insert_iterator; + struct container_extractor : iterator { + container_extractor(iterator it) : iterator(it) {} + using iterator::container; + } extractor(out); + internal::container_buffer buf(*extractor.container); + vformat_to(buf, format_str, args); +} + +template +inline void format_to(std::back_insert_iterator out, + string_view format_str, + const Args & ... args) { + vformat_to(out, format_str, make_args(args...)); } inline std::string vformat(string_view format_str, format_args args) { memory_buffer buffer; vformat_to(buffer, format_str, args); - return to_string(buffer); + return fmt::to_string(buffer); } inline std::wstring vformat(wstring_view format_str, wformat_args args) { diff --git a/include/fmt/printf.h b/include/fmt/printf.h index eadcabea..f3dbaf3e 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -232,8 +232,8 @@ class printf_arg_formatter : public internal::arg_formatter_base { specifier information for standard argument types. \endrst */ - printf_arg_formatter(basic_buffer &buffer, format_specs &spec, - basic_printf_context &ctx) + printf_arg_formatter(internal::basic_buffer &buffer, + format_specs &spec, basic_printf_context &ctx) : base(buffer, spec), context_(ctx) {} using base::operator(); @@ -522,7 +522,7 @@ void basic_printf_context::format() { } template -void printf(basic_buffer &buf, basic_string_view format, +void printf(internal::basic_buffer &buf, basic_string_view format, basic_format_args args) { Context(buf, format, args).format(); } diff --git a/include/fmt/string.h b/include/fmt/string.h deleted file mode 100644 index c9a2eac3..00000000 --- a/include/fmt/string.h +++ /dev/null @@ -1,92 +0,0 @@ -// Formatting library for C++ - string utilities -// -// Copyright (c) 2012 - 2016, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. - -#ifndef FMT_STRING_H_ -#define FMT_STRING_H_ - -#include "fmt/format.h" - -namespace fmt { - -/** - \rst - This class template represents a character buffer backed by std::string. - - You can use one of the following typedefs for common character types - and the standard allocator: - - +----------------+------------------------------+ - | Type | Definition | - +================+==============================+ - | string_buffer | basic_string_buffer | - +----------------+------------------------------+ - | wstring_buffer | basic_string_buffer | - +----------------+------------------------------+ - - **Example**:: - - string_buffer out; - format_to(out, "The answer is {}", 42); - - This will write the following output to the ``out`` object: - - .. code-block:: none - - The answer is 42 - - The output can be moved to an ``std::string`` with ``out.move_to()``. - \endrst - */template -class basic_string_buffer : public basic_buffer { - private: - std::basic_string str_; - - protected: - virtual void grow(std::size_t capacity) { - str_.resize(capacity); - this->set(&str_[0], capacity); - } - - public: - /** - \rst - Moves the buffer content to *str* clearing the buffer. - \endrst - */ - void move_to(std::basic_string &str) { - str_.resize(this->size()); - str.swap(str_); - this->resize(0); - this->set(0, 0); - } -}; - -typedef basic_string_buffer string_buffer; -typedef basic_string_buffer wstring_buffer; - -/** - \rst - Converts *value* to ``std::string`` using the default format for type *T*. - - **Example**:: - - #include "fmt/string.h" - - std::string answer = fmt::to_string(42); - \endrst - */ -template -std::string to_string(const T &value) { - string_buffer buf; - writer(buf).write(value); - std::string str; - buf.move_to(str); - return str; -} -} - -#endif // FMT_STRING_H_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ab6faae1..453a14ec 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -85,7 +85,6 @@ add_fmt_test(format-test) add_fmt_test(format-impl-test) add_fmt_test(ostream-test) add_fmt_test(printf-test) -add_fmt_test(string-test) add_fmt_test(time-test) add_fmt_test(util-test mock-allocator.h) add_fmt_test(custom-formatter-test) diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index 06fe9df6..ef66b1af 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -36,7 +36,7 @@ class CustomArgFormatter : std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) { fmt::memory_buffer buffer; // Pass custom argument formatter as a template arg to vwrite. - fmt::vformat_to(buffer, format_str, args); + fmt::do_vformat_to(buffer, format_str, args); return std::string(buffer.data(), buffer.size()); } diff --git a/test/format-test.cc b/test/format-test.cc index 6296ad61..77d95029 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -88,7 +88,7 @@ void std_format(long double value, std::wstring &result) { template ::testing::AssertionResult check_write(const T &value, const char *type) { fmt::basic_memory_buffer buffer; - using range = fmt::internal::dynamic_range>; + using range = fmt::internal::dynamic_range>; fmt::basic_writer writer(buffer); writer.write(value); std::basic_string actual = to_string(buffer); @@ -1224,7 +1224,7 @@ struct formatter { } auto format(const Date &d, context &ctx) { - format_range(ctx.range(), "{}-{}-{}", d.year(), d.month(), d.day()); + format_to(ctx.begin(), "{}-{}-{}", d.year(), d.month(), d.day()); return ctx.begin(); } }; @@ -1509,7 +1509,7 @@ class mock_arg_formatter : void custom_vformat(fmt::string_view format_str, fmt::format_args args) { fmt::memory_buffer buffer; - fmt::vformat_to(buffer, format_str, args); + fmt::do_vformat_to(buffer, format_str, args); } template @@ -1907,3 +1907,18 @@ TEST(FormatTest, FormatStringErrors) { "cannot switch from automatic to manual argument indexing", int, int); } + +TEST(FormatTest, OutputIterator) { + + std::string s; + fmt::format_to(std::back_inserter(s), "{}", 42); + std::vector v; + fmt::format_to(std::back_inserter(v), "{}", 42); + + EXPECT_EQ("42", s); + EXPECT_EQ("42", std::string(v.begin(), v.end())); +} + +TEST(StringTest, ToString) { + EXPECT_EQ("42", fmt::to_string(42)); +} diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 05993f20..3f93dbd4 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -136,12 +136,12 @@ TEST(OStreamTest, WriteToOStreamMaxSize) { if (max_size <= fmt::internal::to_unsigned(max_streamsize)) return; - struct TestBuffer : fmt::basic_buffer { - explicit TestBuffer(std::size_t size) { resize(size); } + struct test_buffer : fmt::buffer { + explicit test_buffer(std::size_t size) { resize(size); } void grow(std::size_t) {} } buffer(max_size); - struct MockStreamBuf : std::streambuf { + struct mock_streambuf : std::streambuf { MOCK_METHOD2(xsputn, std::streamsize (const void *s, std::streamsize n)); std::streamsize xsputn(const char *s, std::streamsize n) { const void *v = s; @@ -149,16 +149,16 @@ TEST(OStreamTest, WriteToOStreamMaxSize) { } } streambuf; - struct TestOStream : std::ostream { - explicit TestOStream(MockStreamBuf &buffer) : std::ostream(&buffer) {} + struct test_ostream : std::ostream { + explicit test_ostream(mock_streambuf &buffer) : std::ostream(&buffer) {} } os(streambuf); testing::InSequence sequence; const char *data = 0; std::size_t size = max_size; do { - typedef std::make_unsigned::type UStreamSize; - UStreamSize n = std::min( + typedef std::make_unsigned::type ustreamsize; + ustreamsize n = std::min( size, fmt::internal::to_unsigned(max_streamsize)); EXPECT_CALL(streambuf, xsputn(data, static_cast(n))) .WillOnce(testing::Return(max_streamsize)); diff --git a/test/string-test.cc b/test/string-test.cc deleted file mode 100644 index 3c6315d6..00000000 --- a/test/string-test.cc +++ /dev/null @@ -1,71 +0,0 @@ -/* - Tests of string utilities - - Copyright (c) 2012 - 2016, Victor Zverovich - All rights reserved. - - For the license information refer to format.h. - */ - -#include "fmt/string.h" -#include "gtest/gtest.h" - -using fmt::string_buffer; - -TEST(StringBufferTest, Empty) { - string_buffer buffer; - EXPECT_EQ(0u, buffer.size()); - EXPECT_EQ(0u, buffer.capacity()); - std::string data; - // std::string may have initial capacity. - std::size_t capacity = data.capacity(); - buffer.move_to(data); - EXPECT_EQ("", data); - EXPECT_EQ(capacity, data.capacity()); -} - -TEST(StringBufferTest, Reserve) { - string_buffer buffer; - std::size_t capacity = std::string().capacity() + 10; - buffer.reserve(capacity); - EXPECT_EQ(0u, buffer.size()); - EXPECT_EQ(capacity, buffer.capacity()); - std::string data; - buffer.move_to(data); - EXPECT_EQ("", data); -} - -TEST(StringBufferTest, Resize) { - string_buffer buffer; - std::size_t size = std::string().capacity() + 10; - buffer.resize(size); - EXPECT_EQ(size, buffer.size()); - EXPECT_EQ(size, buffer.capacity()); - std::string data; - buffer.move_to(data); - EXPECT_EQ(size, data.size()); -} - -TEST(StringBufferTest, MoveTo) { - string_buffer buffer; - std::size_t size = std::string().capacity() + 10; - buffer.resize(size); - const char *p = &buffer[0]; - std::string data; - buffer.move_to(data); - EXPECT_EQ(p, &data[0]); - EXPECT_EQ(0u, buffer.size()); - EXPECT_EQ(0u, buffer.capacity()); -} - -TEST(StringBufferTest, WString) { - fmt::wstring_buffer out; - out.push_back(L'x'); - std::wstring s; - out.move_to(s); - EXPECT_EQ(L"x", s); -} - -TEST(StringTest, ToString) { - EXPECT_EQ("42", fmt::to_string(42)); -} diff --git a/test/util-test.cc b/test/util-test.cc index d23107ab..42399e78 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -54,7 +54,7 @@ #undef max using fmt::basic_arg; -using fmt::basic_buffer; +using fmt::internal::basic_buffer; using fmt::basic_memory_buffer; using fmt::string_view; using fmt::internal::value; @@ -192,7 +192,7 @@ TEST(BufferTest, Access) { EXPECT_EQ(11, buffer[0]); buffer[3] = 42; EXPECT_EQ(42, *(&buffer[0] + 3)); - const fmt::basic_buffer &const_buffer = buffer; + const basic_buffer &const_buffer = buffer; EXPECT_EQ(42, const_buffer[3]); }