// Formatting library for C++ - std::ostream support // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_OSTREAM_H_ #define FMT_OSTREAM_H_ #include <ostream> #include "format.h" FMT_BEGIN_NAMESPACE template <typename Char> class basic_printf_parse_context; template <typename OutputIt, typename Char> class basic_printf_context; namespace detail { template <class Char> class formatbuf : public std::basic_streambuf<Char> { private: using int_type = typename std::basic_streambuf<Char>::int_type; using traits_type = typename std::basic_streambuf<Char>::traits_type; buffer<Char>& buffer_; public: formatbuf(buffer<Char>& buf) : buffer_(buf) {} protected: // The put-area is actually 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. The obvious // 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. int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { if (!traits_type::eq_int_type(ch, traits_type::eof())) buffer_.push_back(static_cast<Char>(ch)); return ch; } std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { buffer_.append(s, s + count); return count; } }; struct converter { template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T); }; template <typename Char> struct test_stream : std::basic_ostream<Char> { private: void_t<> operator<<(converter); }; // Hide insertion operators for built-in types. template <typename Char, typename Traits> void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char); template <typename Char, typename Traits> void_t<> operator<<(std::basic_ostream<Char, Traits>&, char); template <typename Traits> void_t<> operator<<(std::basic_ostream<char, Traits>&, char); template <typename Traits> void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char); template <typename Traits> void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char); // Checks if T has a user-defined operator<< (e.g. not a member of // std::ostream). template <typename T, typename Char> class is_streamable { private: template <typename U> static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>() << std::declval<U>()), void_t<>>::value> test(int); template <typename> static std::false_type test(...); using result = decltype(test<T>(0)); public: is_streamable() = default; static const bool value = result::value; }; // Write the content of buf to os. template <typename Char> void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { const Char* buf_data = buf.data(); using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; unsigned_streamsize size = buf.size(); unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); do { unsigned_streamsize n = size <= max_size ? size : max_size; os.write(buf_data, static_cast<std::streamsize>(n)); buf_data += n; size -= n; } while (size != 0); } template <typename Char, typename T> void format_value(buffer<Char>& buf, const T& value, locale_ref loc = locale_ref()) { formatbuf<Char> format_buf(buf); std::basic_ostream<Char> output(&format_buf); #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) if (loc) output.imbue(loc.get<std::locale>()); #endif output << value; output.exceptions(std::ios_base::failbit | std::ios_base::badbit); buf.try_resize(buf.size()); } // Formats an object of type T that has an overloaded ostream operator<<. template <typename T, typename Char> struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> : private formatter<basic_string_view<Char>, Char> { FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()) { return formatter<basic_string_view<Char>, Char>::parse(ctx); } template <typename ParseCtx, FMT_ENABLE_IF(std::is_same< ParseCtx, basic_printf_parse_context<Char>>::value)> auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) { return ctx.begin(); } template <typename OutputIt> auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) -> OutputIt { basic_memory_buffer<Char> buffer; format_value(buffer, value, ctx.locale()); basic_string_view<Char> str(buffer.data(), buffer.size()); return formatter<basic_string_view<Char>, Char>::format(str, ctx); } template <typename OutputIt> auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) -> OutputIt { basic_memory_buffer<Char> buffer; format_value(buffer, value, ctx.locale()); return std::copy(buffer.begin(), buffer.end(), ctx.out()); } }; } // namespace detail FMT_MODULE_EXPORT template <typename Char> void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_memory_buffer<Char> buffer; detail::vformat_to(buffer, format_str, args); detail::write_buffer(os, buffer); } /** \rst Prints formatted data to the stream *os*. **Example**:: fmt::print(cerr, "Don't {}!", "panic"); \endrst */ FMT_MODULE_EXPORT template <typename S, typename... Args, typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { vprint(os, to_string_view(format_str), fmt::make_args_checked<Args...>(format_str, args...)); } FMT_END_NAMESPACE #endif // FMT_OSTREAM_H_