diff --git a/include/fmt/format.h b/include/fmt/format.h index 3cf123ee..52b16779 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -265,6 +265,38 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE namespace detail { + +template class formatbuf : public Streambuf { + private: + using char_type = typename Streambuf::char_type; + using streamsize = decltype(std::declval().sputn(nullptr, 0)); + using int_type = typename Streambuf::int_type; + using traits_type = typename Streambuf::traits_type; + + buffer& buffer_; + + public: + explicit formatbuf(buffer& buf) : buffer_(buf) {} + + protected: + // The put area is always empty. This makes the implementation simpler and has + // the advantage that the streambuf and the buffer are always in sync and + // sputc never writes into uninitialized memory. A disadvantage is that each + // call to sputc always results in a (virtual) call to overflow. There is no + // disadvantage here for sputn since this always results in a call to xsputn. + + auto overflow(int_type ch) -> int_type override { + if (!traits_type::eq_int_type(ch, traits_type::eof())) + buffer_.push_back(static_cast(ch)); + return ch; + } + + auto xsputn(const char_type* s, streamsize count) -> streamsize override { + buffer_.append(s, s + count); + return count; + } +}; + // An equivalent of `*reinterpret_cast(&source)` that doesn't have // undefined behavior (e.g. due to type aliasing). // Example: uint64_t d = bit_cast(2.718); diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index dd005fa1..0ebdd60f 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -18,36 +18,6 @@ template class basic_printf_context; namespace detail { -template class formatbuf : public std::basic_streambuf { - private: - using int_type = typename std::basic_streambuf::int_type; - using traits_type = typename std::basic_streambuf::traits_type; - - buffer& buffer_; - - public: - explicit formatbuf(buffer& buf) : buffer_(buf) {} - - protected: - // The put area is always empty. This makes the implementation simpler and has - // the advantage that the streambuf and the buffer are always in sync and - // sputc never writes into uninitialized memory. A disadvantage is that each - // call to sputc always results in a (virtual) call to overflow. There is no - // disadvantage here for sputn since this always results in a call to xsputn. - - auto overflow(int_type ch = traits_type::eof()) -> int_type override { - if (!traits_type::eq_int_type(ch, traits_type::eof())) - buffer_.push_back(static_cast(ch)); - return ch; - } - - auto xsputn(const Char* s, std::streamsize count) - -> std::streamsize override { - buffer_.append(s, s + count); - return count; - } -}; - // Checks if T has a user-defined operator<<. template class is_streamable { @@ -97,7 +67,7 @@ void write_buffer(std::basic_ostream& os, buffer& buf) { template void format_value(buffer& buf, const T& value, locale_ref loc = locale_ref()) { - auto&& format_buf = formatbuf(buf); + auto&& format_buf = formatbuf>(buf); auto&& output = std::basic_ostream(&format_buf); #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) if (loc) output.imbue(loc.get());