| 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 | #ifndef FMT_MODULE |
| 12 | # include <fstream> // std::filebuf |
| 13 | #endif |
| 14 | |
| 15 | #ifdef _WIN32 |
| 16 | # ifdef __GLIBCXX__ |
| 17 | # include <ext/stdio_filebuf.h> |
| 18 | # include <ext/stdio_sync_filebuf.h> |
| 19 | # endif |
| 20 | # include <io.h> |
| 21 | #endif |
| 22 | |
| 23 | #include "chrono.h" // formatbuf |
| 24 | |
| 25 | FMT_BEGIN_NAMESPACE |
| 26 | namespace detail { |
| 27 | |
| 28 | // Generate a unique explicit instantion in every translation unit using a tag |
| 29 | // type in an anonymous namespace. |
| 30 | namespace { |
| 31 | struct file_access_tag {}; |
| 32 | } // namespace |
| 33 | template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr> |
| 34 | class file_access { |
| 35 | friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } |
| 36 | }; |
| 37 | |
| 38 | #if FMT_MSC_VERSION |
| 39 | template class file_access<file_access_tag, std::filebuf, |
| 40 | &std::filebuf::_Myfile>; |
| 41 | auto get_file(std::filebuf&) -> FILE*; |
| 42 | #endif |
| 43 | |
| 44 | inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data) |
| 45 | -> bool { |
| 46 | FILE* f = nullptr; |
| 47 | #if FMT_MSC_VERSION && FMT_USE_RTTI |
| 48 | if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf())) |
| 49 | f = get_file(*buf); |
| 50 | else |
| 51 | return false; |
| 52 | #elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI |
| 53 | auto* rdbuf = os.rdbuf(); |
| 54 | if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) |
| 55 | f = sfbuf->file(); |
| 56 | else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) |
| 57 | f = fbuf->file(); |
| 58 | else |
| 59 | return false; |
| 60 | #else |
| 61 | ignore_unused(os, data, f); |
| 62 | #endif |
| 63 | #ifdef _WIN32 |
| 64 | if (f) { |
| 65 | int fd = _fileno(f); |
| 66 | if (_isatty(fd)) { |
| 67 | os.flush(); |
| 68 | return write_console(fd, data); |
| 69 | } |
| 70 | } |
| 71 | #endif |
| 72 | return false; |
| 73 | } |
| 74 | inline auto write_ostream_unicode(std::wostream&, |
| 75 | fmt::basic_string_view<wchar_t>) -> bool { |
| 76 | return false; |
| 77 | } |
| 78 | |
| 79 | // Write the content of buf to os. |
| 80 | // It is a separate function rather than a part of vprint to simplify testing. |
| 81 | template <typename Char> |
| 82 | void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { |
| 83 | const Char* buf_data = buf.data(); |
| 84 | using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; |
| 85 | unsigned_streamsize size = buf.size(); |
| 86 | unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); |
| 87 | do { |
| 88 | unsigned_streamsize n = size <= max_size ? size : max_size; |
| 89 | os.write(buf_data, static_cast<std::streamsize>(n)); |
| 90 | buf_data += n; |
| 91 | size -= n; |
| 92 | } while (size != 0); |
| 93 | } |
| 94 | |
| 95 | template <typename Char, typename T> |
| 96 | void format_value(buffer<Char>& buf, const T& value) { |
| 97 | auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); |
| 98 | auto&& output = std::basic_ostream<Char>(&format_buf); |
| 99 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) |
| 100 | output.imbue(std::locale::classic()); // The default is always unlocalized. |
| 101 | #endif |
| 102 | output << value; |
| 103 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); |
| 104 | } |
| 105 | |
| 106 | template <typename T> struct streamed_view { |
| 107 | const T& value; |
| 108 | }; |
| 109 | |
| 110 | } // namespace detail |
| 111 | |
| 112 | // Formats an object of type T that has an overloaded ostream operator<<. |
| 113 | template <typename Char> |
| 114 | struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { |
| 115 | void set_debug_format() = delete; |
| 116 | |
| 117 | template <typename T, typename Context> |
| 118 | auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) { |
| 119 | auto buffer = basic_memory_buffer<Char>(); |
| 120 | detail::format_value(buffer, value); |
| 121 | return formatter<basic_string_view<Char>, Char>::format( |
| 122 | {buffer.data(), buffer.size()}, ctx); |
| 123 | } |
| 124 | }; |
| 125 | |
| 126 | using ostream_formatter = basic_ostream_formatter<char>; |
| 127 | |
| 128 | template <typename T, typename Char> |
| 129 | struct formatter<detail::streamed_view<T>, Char> |
| 130 | : basic_ostream_formatter<Char> { |
| 131 | template <typename Context> |
| 132 | auto format(detail::streamed_view<T> view, Context& ctx) const |
| 133 | -> decltype(ctx.out()) { |
| 134 | return basic_ostream_formatter<Char>::format(view.value, ctx); |
| 135 | } |
| 136 | }; |
| 137 | |
| 138 | /** |
| 139 | * Returns a view that formats `value` via an ostream `operator<<`. |
| 140 | * |
| 141 | * **Example**: |
| 142 | * |
| 143 | * fmt::print("Current thread id: {}\n", |
| 144 | * fmt::streamed(std::this_thread::get_id())); |
| 145 | */ |
| 146 | template <typename T> |
| 147 | constexpr auto streamed(const T& value) -> detail::streamed_view<T> { |
| 148 | return {value}; |
| 149 | } |
| 150 | |
| 151 | namespace detail { |
| 152 | |
| 153 | inline void vprint_directly(std::ostream& os, string_view format_str, |
| 154 | format_args args) { |
| 155 | auto buffer = memory_buffer(); |
| 156 | detail::vformat_to(buffer, format_str, args); |
| 157 | detail::write_buffer(os, buffer); |
| 158 | } |
| 159 | |
| 160 | } // namespace detail |
| 161 | |
| 162 | FMT_EXPORT template <typename Char> |
| 163 | void vprint(std::basic_ostream<Char>& os, |
| 164 | basic_string_view<type_identity_t<Char>> format_str, |
| 165 | typename detail::vformat_args<Char>::type args) { |
| 166 | auto buffer = basic_memory_buffer<Char>(); |
| 167 | detail::vformat_to(buffer, format_str, args); |
| 168 | if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; |
| 169 | detail::write_buffer(os, buffer); |
| 170 | } |
| 171 | |
| 172 | /** |
| 173 | * Prints formatted data to the stream `os`. |
| 174 | * |
| 175 | * **Example**: |
| 176 | * |
| 177 | * fmt::print(cerr, "Don't {}!", "panic"); |
| 178 | */ |
| 179 | FMT_EXPORT template <typename... T> |
| 180 | void print(std::ostream& os, format_string<T...> fmt, T&&... args) { |
| 181 | const auto& vargs = fmt::make_format_args(args...); |
| 182 | if (detail::use_utf8()) |
| 183 | vprint(os, fmt, vargs); |
| 184 | else |
| 185 | detail::vprint_directly(os, fmt, vargs); |
| 186 | } |
| 187 | |
| 188 | FMT_EXPORT |
| 189 | template <typename... Args> |
| 190 | void print(std::wostream& os, |
| 191 | basic_format_string<wchar_t, type_identity_t<Args>...> fmt, |
| 192 | Args&&... args) { |
| 193 | vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...)); |
| 194 | } |
| 195 | |
| 196 | FMT_EXPORT template <typename... T> |
| 197 | void println(std::ostream& os, format_string<T...> fmt, T&&... args) { |
| 198 | fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...)); |
| 199 | } |
| 200 | |
| 201 | FMT_EXPORT |
| 202 | template <typename... Args> |
| 203 | void println(std::wostream& os, |
| 204 | basic_format_string<wchar_t, type_identity_t<Args>...> fmt, |
| 205 | Args&&... args) { |
| 206 | print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...)); |
| 207 | } |
| 208 | |
| 209 | FMT_END_NAMESPACE |
| 210 | |
| 211 | #endif // FMT_OSTREAM_H_ |