Martin Stjernholm | 4fb5111 | 2021-04-30 11:53:52 +0100 | [diff] [blame^] | 1 | // Formatting library for C++ - std::ostream support |
| 2 | // |
| 3 | // Copyright (c) 2012 - present, Victor Zverovich |
| 4 | // All rights reserved. |
| 5 | // |
| 6 | // For the license information refer to format.h. |
| 7 | |
| 8 | #ifndef FMT_OSTREAM_H_ |
| 9 | #define FMT_OSTREAM_H_ |
| 10 | |
| 11 | #include <ostream> |
| 12 | |
| 13 | #include "format.h" |
| 14 | |
| 15 | FMT_BEGIN_NAMESPACE |
| 16 | |
| 17 | template <typename Char> class basic_printf_parse_context; |
| 18 | template <typename OutputIt, typename Char> class basic_printf_context; |
| 19 | |
| 20 | namespace detail { |
| 21 | |
| 22 | template <class Char> class formatbuf : public std::basic_streambuf<Char> { |
| 23 | private: |
| 24 | using int_type = typename std::basic_streambuf<Char>::int_type; |
| 25 | using traits_type = typename std::basic_streambuf<Char>::traits_type; |
| 26 | |
| 27 | buffer<Char>& buffer_; |
| 28 | |
| 29 | public: |
| 30 | formatbuf(buffer<Char>& buf) : buffer_(buf) {} |
| 31 | |
| 32 | protected: |
| 33 | // The put-area is actually always empty. This makes the implementation |
| 34 | // simpler and has the advantage that the streambuf and the buffer are always |
| 35 | // in sync and sputc never writes into uninitialized memory. The obvious |
| 36 | // disadvantage is that each call to sputc always results in a (virtual) call |
| 37 | // to overflow. There is no disadvantage here for sputn since this always |
| 38 | // results in a call to xsputn. |
| 39 | |
| 40 | int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { |
| 41 | if (!traits_type::eq_int_type(ch, traits_type::eof())) |
| 42 | buffer_.push_back(static_cast<Char>(ch)); |
| 43 | return ch; |
| 44 | } |
| 45 | |
| 46 | std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { |
| 47 | buffer_.append(s, s + count); |
| 48 | return count; |
| 49 | } |
| 50 | }; |
| 51 | |
| 52 | struct converter { |
| 53 | template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T); |
| 54 | }; |
| 55 | |
| 56 | template <typename Char> struct test_stream : std::basic_ostream<Char> { |
| 57 | private: |
| 58 | void_t<> operator<<(converter); |
| 59 | }; |
| 60 | |
| 61 | // Hide insertion operators for built-in types. |
| 62 | template <typename Char, typename Traits> |
| 63 | void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char); |
| 64 | template <typename Char, typename Traits> |
| 65 | void_t<> operator<<(std::basic_ostream<Char, Traits>&, char); |
| 66 | template <typename Traits> |
| 67 | void_t<> operator<<(std::basic_ostream<char, Traits>&, char); |
| 68 | template <typename Traits> |
| 69 | void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char); |
| 70 | template <typename Traits> |
| 71 | void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char); |
| 72 | |
| 73 | // Checks if T has a user-defined operator<< (e.g. not a member of |
| 74 | // std::ostream). |
| 75 | template <typename T, typename Char> class is_streamable { |
| 76 | private: |
| 77 | template <typename U> |
| 78 | static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>() |
| 79 | << std::declval<U>()), |
| 80 | void_t<>>::value> |
| 81 | test(int); |
| 82 | |
| 83 | template <typename> static std::false_type test(...); |
| 84 | |
| 85 | using result = decltype(test<T>(0)); |
| 86 | |
| 87 | public: |
| 88 | static const bool value = result::value; |
| 89 | }; |
| 90 | |
| 91 | // Write the content of buf to os. |
| 92 | template <typename Char> |
| 93 | void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { |
| 94 | const Char* buf_data = buf.data(); |
| 95 | using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; |
| 96 | unsigned_streamsize size = buf.size(); |
| 97 | unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); |
| 98 | do { |
| 99 | unsigned_streamsize n = size <= max_size ? size : max_size; |
| 100 | os.write(buf_data, static_cast<std::streamsize>(n)); |
| 101 | buf_data += n; |
| 102 | size -= n; |
| 103 | } while (size != 0); |
| 104 | } |
| 105 | |
| 106 | template <typename Char, typename T> |
| 107 | void format_value(buffer<Char>& buf, const T& value, |
| 108 | locale_ref loc = locale_ref()) { |
| 109 | formatbuf<Char> format_buf(buf); |
| 110 | std::basic_ostream<Char> output(&format_buf); |
| 111 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) |
| 112 | if (loc) output.imbue(loc.get<std::locale>()); |
| 113 | #endif |
| 114 | output << value; |
| 115 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); |
| 116 | buf.try_resize(buf.size()); |
| 117 | } |
| 118 | |
| 119 | // Formats an object of type T that has an overloaded ostream operator<<. |
| 120 | template <typename T, typename Char> |
| 121 | struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> |
| 122 | : private formatter<basic_string_view<Char>, Char> { |
| 123 | FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) |
| 124 | -> decltype(ctx.begin()) { |
| 125 | return formatter<basic_string_view<Char>, Char>::parse(ctx); |
| 126 | } |
| 127 | template <typename ParseCtx, |
| 128 | FMT_ENABLE_IF(std::is_same< |
| 129 | ParseCtx, basic_printf_parse_context<Char>>::value)> |
| 130 | auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) { |
| 131 | return ctx.begin(); |
| 132 | } |
| 133 | |
| 134 | template <typename OutputIt> |
| 135 | auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) |
| 136 | -> OutputIt { |
| 137 | basic_memory_buffer<Char> buffer; |
| 138 | format_value(buffer, value, ctx.locale()); |
| 139 | basic_string_view<Char> str(buffer.data(), buffer.size()); |
| 140 | return formatter<basic_string_view<Char>, Char>::format(str, ctx); |
| 141 | } |
| 142 | template <typename OutputIt> |
| 143 | auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) |
| 144 | -> OutputIt { |
| 145 | basic_memory_buffer<Char> buffer; |
| 146 | format_value(buffer, value, ctx.locale()); |
| 147 | return std::copy(buffer.begin(), buffer.end(), ctx.out()); |
| 148 | } |
| 149 | }; |
| 150 | } // namespace detail |
| 151 | |
| 152 | template <typename Char> |
| 153 | void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, |
| 154 | basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
| 155 | basic_memory_buffer<Char> buffer; |
| 156 | detail::vformat_to(buffer, format_str, args); |
| 157 | detail::write_buffer(os, buffer); |
| 158 | } |
| 159 | |
| 160 | /** |
| 161 | \rst |
| 162 | Prints formatted data to the stream *os*. |
| 163 | |
| 164 | **Example**:: |
| 165 | |
| 166 | fmt::print(cerr, "Don't {}!", "panic"); |
| 167 | \endrst |
| 168 | */ |
| 169 | template <typename S, typename... Args, |
| 170 | typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> |
| 171 | void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { |
| 172 | vprint(os, to_string_view(format_str), |
| 173 | fmt::make_args_checked<Args...>(format_str, args...)); |
| 174 | } |
| 175 | FMT_END_NAMESPACE |
| 176 | |
| 177 | #endif // FMT_OSTREAM_H_ |