| 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 <fstream> |
| 12 | #include <ostream> |
| 13 | #if defined(_WIN32) && defined(__GLIBCXX__) |
| 14 | # include <ext/stdio_filebuf.h> |
| 15 | # include <ext/stdio_sync_filebuf.h> |
| 16 | #elif defined(_WIN32) && defined(_LIBCPP_VERSION) |
| 17 | # include <__std_stream> |
| 18 | #endif |
| 19 | |
| 20 | #include "format.h" |
| 21 | |
| 22 | FMT_BEGIN_NAMESPACE |
| 23 | |
| 24 | template <typename OutputIt, typename Char> class basic_printf_context; |
| 25 | |
| 26 | namespace detail { |
| 27 | |
| 28 | // Checks if T has a user-defined operator<<. |
| 29 | template <typename T, typename Char, typename Enable = void> |
| 30 | class is_streamable { |
| 31 | private: |
| 32 | template <typename U> |
| 33 | static auto test(int) |
| 34 | -> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>() |
| 35 | << std::declval<U>()) != 0>; |
| 36 | |
| 37 | template <typename> static auto test(...) -> std::false_type; |
| 38 | |
| 39 | using result = decltype(test<T>(0)); |
| 40 | |
| 41 | public: |
| 42 | is_streamable() = default; |
| 43 | |
| 44 | static const bool value = result::value; |
| 45 | }; |
| 46 | |
| 47 | // Formatting of built-in types and arrays is intentionally disabled because |
| 48 | // it's handled by standard (non-ostream) formatters. |
| 49 | template <typename T, typename Char> |
| 50 | struct is_streamable< |
| 51 | T, Char, |
| 52 | enable_if_t< |
| 53 | std::is_arithmetic<T>::value || std::is_array<T>::value || |
| 54 | std::is_pointer<T>::value || std::is_same<T, char8_type>::value || |
| 55 | std::is_convertible<T, fmt::basic_string_view<Char>>::value || |
| 56 | std::is_same<T, std_string_view<Char>>::value || |
| 57 | (std::is_convertible<T, int>::value && !std::is_enum<T>::value)>> |
| 58 | : std::false_type {}; |
| 59 | |
| 60 | // Generate a unique explicit instantion in every translation unit using a tag |
| 61 | // type in an anonymous namespace. |
| 62 | namespace { |
| 63 | struct file_access_tag {}; |
| 64 | } // namespace |
| 65 | template <class Tag, class BufType, FILE* BufType::*FileMemberPtr> |
| 66 | class file_access { |
| 67 | friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } |
| 68 | }; |
| 69 | |
| 70 | #if FMT_MSC_VERSION |
| 71 | template class file_access<file_access_tag, std::filebuf, |
| 72 | &std::filebuf::_Myfile>; |
| 73 | auto get_file(std::filebuf&) -> FILE*; |
| 74 | #elif defined(_WIN32) && defined(_LIBCPP_VERSION) |
| 75 | template class file_access<file_access_tag, std::__stdoutbuf<char>, |
| 76 | &std::__stdoutbuf<char>::__file_>; |
| 77 | auto get_file(std::__stdoutbuf<char>&) -> FILE*; |
| 78 | #endif |
| 79 | |
| 80 | inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { |
| 81 | #if FMT_MSC_VERSION |
| 82 | if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) |
| 83 | if (FILE* f = get_file(*buf)) return write_console(f, data); |
| 84 | #elif defined(_WIN32) && defined(__GLIBCXX__) |
| 85 | auto* rdbuf = os.rdbuf(); |
| 86 | FILE* c_file; |
| 87 | if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) |
| 88 | c_file = fbuf->file(); |
| 89 | else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) |
| 90 | c_file = fbuf->file(); |
| 91 | else |
| 92 | return false; |
| 93 | if (c_file) return write_console(c_file, data); |
| 94 | #elif defined(_WIN32) && defined(_LIBCPP_VERSION) |
| 95 | if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf())) |
| 96 | if (FILE* f = get_file(*buf)) return write_console(f, data); |
| 97 | #else |
| 98 | ignore_unused(os, data); |
| 99 | #endif |
| 100 | return false; |
| 101 | } |
| 102 | inline bool write_ostream_unicode(std::wostream&, |
| 103 | fmt::basic_string_view<wchar_t>) { |
| 104 | return false; |
| 105 | } |
| 106 | |
| 107 | // Write the content of buf to os. |
| 108 | // It is a separate function rather than a part of vprint to simplify testing. |
| 109 | template <typename Char> |
| 110 | void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { |
| 111 | const Char* buf_data = buf.data(); |
| 112 | using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; |
| 113 | unsigned_streamsize size = buf.size(); |
| 114 | unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); |
| 115 | do { |
| 116 | unsigned_streamsize n = size <= max_size ? size : max_size; |
| 117 | os.write(buf_data, static_cast<std::streamsize>(n)); |
| 118 | buf_data += n; |
| 119 | size -= n; |
| 120 | } while (size != 0); |
| 121 | } |
| 122 | |
| 123 | template <typename Char, typename T> |
| 124 | void format_value(buffer<Char>& buf, const T& value, |
| 125 | locale_ref loc = locale_ref()) { |
| 126 | auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); |
| 127 | auto&& output = std::basic_ostream<Char>(&format_buf); |
| 128 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) |
| 129 | if (loc) output.imbue(loc.get<std::locale>()); |
| 130 | #endif |
| 131 | output << value; |
| 132 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); |
| 133 | } |
| 134 | |
| 135 | template <typename T> struct streamed_view { const T& value; }; |
| 136 | |
| 137 | } // namespace detail |
| 138 | |
| 139 | // Formats an object of type T that has an overloaded ostream operator<<. |
| 140 | template <typename Char> |
| 141 | struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { |
| 142 | void set_debug_format() = delete; |
| 143 | |
| 144 | template <typename T, typename OutputIt> |
| 145 | auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const |
| 146 | -> OutputIt { |
| 147 | auto buffer = basic_memory_buffer<Char>(); |
| 148 | format_value(buffer, value, ctx.locale()); |
| 149 | return formatter<basic_string_view<Char>, Char>::format( |
| 150 | {buffer.data(), buffer.size()}, ctx); |
| 151 | } |
| 152 | }; |
| 153 | |
| 154 | using ostream_formatter = basic_ostream_formatter<char>; |
| 155 | |
| 156 | template <typename T, typename Char> |
| 157 | struct formatter<detail::streamed_view<T>, Char> |
| 158 | : basic_ostream_formatter<Char> { |
| 159 | template <typename OutputIt> |
| 160 | auto format(detail::streamed_view<T> view, |
| 161 | basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { |
| 162 | return basic_ostream_formatter<Char>::format(view.value, ctx); |
| 163 | } |
| 164 | }; |
| 165 | |
| 166 | /** |
| 167 | \rst |
| 168 | Returns a view that formats `value` via an ostream ``operator<<``. |
| 169 | |
| 170 | **Example**:: |
| 171 | |
| 172 | fmt::print("Current thread id: {}\n", |
| 173 | fmt::streamed(std::this_thread::get_id())); |
| 174 | \endrst |
| 175 | */ |
| 176 | template <typename T> |
| 177 | auto streamed(const T& value) -> detail::streamed_view<T> { |
| 178 | return {value}; |
| 179 | } |
| 180 | |
| 181 | namespace detail { |
| 182 | |
| 183 | // Formats an object of type T that has an overloaded ostream operator<<. |
| 184 | template <typename T, typename Char> |
| 185 | struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> |
| 186 | : basic_ostream_formatter<Char> { |
| 187 | using basic_ostream_formatter<Char>::format; |
| 188 | }; |
| 189 | |
| 190 | inline void vprint_directly(std::ostream& os, string_view format_str, |
| 191 | format_args args) { |
| 192 | auto buffer = memory_buffer(); |
| 193 | detail::vformat_to(buffer, format_str, args); |
| 194 | detail::write_buffer(os, buffer); |
| 195 | } |
| 196 | |
| 197 | } // namespace detail |
| 198 | |
| 199 | FMT_MODULE_EXPORT template <typename Char> |
| 200 | void vprint(std::basic_ostream<Char>& os, |
| 201 | basic_string_view<type_identity_t<Char>> format_str, |
| 202 | basic_format_args<buffer_context<type_identity_t<Char>>> args) { |
| 203 | auto buffer = basic_memory_buffer<Char>(); |
| 204 | detail::vformat_to(buffer, format_str, args); |
| 205 | if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; |
| 206 | detail::write_buffer(os, buffer); |
| 207 | } |
| 208 | |
| 209 | /** |
| 210 | \rst |
| 211 | Prints formatted data to the stream *os*. |
| 212 | |
| 213 | **Example**:: |
| 214 | |
| 215 | fmt::print(cerr, "Don't {}!", "panic"); |
| 216 | \endrst |
| 217 | */ |
| 218 | FMT_MODULE_EXPORT template <typename... T> |
| 219 | void print(std::ostream& os, format_string<T...> fmt, T&&... args) { |
| 220 | const auto& vargs = fmt::make_format_args(args...); |
| 221 | if (detail::is_utf8()) |
| 222 | vprint(os, fmt, vargs); |
| 223 | else |
| 224 | detail::vprint_directly(os, fmt, vargs); |
| 225 | } |
| 226 | |
| 227 | FMT_MODULE_EXPORT |
| 228 | template <typename... Args> |
| 229 | void print(std::wostream& os, |
| 230 | basic_format_string<wchar_t, type_identity_t<Args>...> fmt, |
| 231 | Args&&... args) { |
| 232 | vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); |
| 233 | } |
| 234 | |
| 235 | FMT_END_NAMESPACE |
| 236 | |
| 237 | #endif // FMT_OSTREAM_H_ |