diff options
author | David Robillard <d@drobilla.net> | 2022-08-16 16:44:29 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-08-16 16:44:29 -0400 |
commit | 3e5cf3400273b0e4f8f96a3a87d2b9e57574eeec (patch) | |
tree | cee5b7cd84248dea488653ca874b40dd8b53c06d /subprojects/fmt/include/fmt/ostream.h | |
parent | 66e0bb76b40311245b9c59e2f3167e4e98027eba (diff) | |
download | patchage-3e5cf3400273b0e4f8f96a3a87d2b9e57574eeec.tar.gz patchage-3e5cf3400273b0e4f8f96a3a87d2b9e57574eeec.tar.bz2 patchage-3e5cf3400273b0e4f8f96a3a87d2b9e57574eeec.zip |
Upgrade to fmt 9.0.0
This library tends to break in annoying ways like this, so pin the major
version to 9 to hopefully avoid these problems in the future.
Diffstat (limited to 'subprojects/fmt/include/fmt/ostream.h')
-rw-r--r-- | subprojects/fmt/include/fmt/ostream.h | 224 |
1 files changed, 130 insertions, 94 deletions
diff --git a/subprojects/fmt/include/fmt/ostream.h b/subprojects/fmt/include/fmt/ostream.h index 29c58ec..394d947 100644 --- a/subprojects/fmt/include/fmt/ostream.h +++ b/subprojects/fmt/include/fmt/ostream.h @@ -8,89 +8,97 @@ #ifndef FMT_OSTREAM_H_ #define FMT_OSTREAM_H_ +#include <fstream> #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> { +// Checks if T has a user-defined operator<<. +template <typename T, typename Char, typename Enable = void> +class is_streamable { private: - using int_type = typename std::basic_streambuf<Char>::int_type; - using traits_type = typename std::basic_streambuf<Char>::traits_type; + template <typename U> + static auto test(int) + -> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>() + << std::declval<U>()) != 0>; + + template <typename> static auto test(...) -> std::false_type; - buffer<Char>& buffer_; + using result = decltype(test<T>(0)); 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; - } + is_streamable() = default; - std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { - buffer_.append(s, s + count); - return count; - } + static const bool value = result::value; }; -struct converter { - template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T); -}; +// Formatting of built-in types and arrays is intentionally disabled because +// it's handled by standard (non-ostream) formatters. +template <typename T, typename Char> +struct is_streamable< + T, Char, + enable_if_t< + std::is_arithmetic<T>::value || std::is_array<T>::value || + std::is_pointer<T>::value || std::is_same<T, char8_type>::value || + std::is_convertible<T, fmt::basic_string_view<Char>>::value || + std::is_same<T, std_string_view<Char>>::value || + (std::is_convertible<T, int>::value && !std::is_enum<T>::value)>> + : std::false_type {}; + +template <typename Char> FILE* get_file(std::basic_filebuf<Char>&) { + return nullptr; +} -template <typename Char> struct test_stream : std::basic_ostream<Char> { - private: - void_t<> operator<<(converter); +struct dummy_filebuf { + FILE* _Myfile; }; - -// 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: - static const bool value = result::value; +template <typename T, typename U = int> struct ms_filebuf { + using type = dummy_filebuf; }; +template <typename T> struct ms_filebuf<T, decltype(T::_Myfile, 0)> { + using type = T; +}; +using filebuf_type = ms_filebuf<std::filebuf>::type; + +FILE* get_file(filebuf_type& buf); + +// Generate a unique explicit instantion in every translation unit using a tag +// type in an anonymous namespace. +namespace { +struct filebuf_access_tag {}; +} // namespace +template <typename Tag, typename FileMemberPtr, FileMemberPtr file> +class filebuf_access { + friend FILE* get_file(filebuf_type& buf) { return buf.*file; } +}; +template class filebuf_access<filebuf_access_tag, + decltype(&filebuf_type::_Myfile), + &filebuf_type::_Myfile>; + +inline bool write(std::filebuf& buf, fmt::string_view data) { + FILE* f = get_file(buf); + if (!f) return false; + print(f, data); + return true; +} +inline bool write(std::wfilebuf&, fmt::basic_string_view<wchar_t>) { + return false; +} // Write the content of buf to os. +// It is a separate function rather than a part of vprint to simplify testing. template <typename Char> void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { + if (const_check(FMT_MSC_VERSION)) { + auto filebuf = dynamic_cast<std::basic_filebuf<Char>*>(os.rdbuf()); + if (filebuf && write(*filebuf, {buf.data(), buf.size()})) return; + } const Char* buf_data = buf.data(); using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; unsigned_streamsize size = buf.size(); @@ -106,53 +114,74 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { 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); + auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf); + auto&& output = std::basic_ostream<Char>(&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 T> struct streamed_view { const T& value; }; - template <typename OutputIt> - auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) +} // namespace detail + +// Formats an object of type T that has an overloaded ostream operator<<. +template <typename Char> +struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { + template <typename T, typename OutputIt> + auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { - basic_memory_buffer<Char> buffer; + auto buffer = basic_memory_buffer<Char>(); 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); + return formatter<basic_string_view<Char>, Char>::format( + {buffer.data(), buffer.size()}, ctx); } +}; + +using ostream_formatter = basic_ostream_formatter<char>; + +template <typename T> +struct formatter<detail::streamed_view<T>> : ostream_formatter { 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()); + auto format(detail::streamed_view<T> view, + basic_format_context<OutputIt, char>& ctx) const -> OutputIt { + return ostream_formatter::format(view.value, ctx); } }; + +/** + \rst + Returns a view that formats `value` via an ostream ``operator<<``. + + **Example**:: + + fmt::print("Current thread id: {}\n", + fmt::streamed(std::this_thread::get_id())); + \endrst + */ +template <typename T> +auto streamed(const T& value) -> detail::streamed_view<T> { + return {value}; +} + +namespace detail { + +// 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>> + : basic_ostream_formatter<Char> { + using basic_ostream_formatter<Char>::format; +}; + } // namespace detail -template <typename Char> -void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, +FMT_MODULE_EXPORT template <typename Char> +void vprint(std::basic_ostream<Char>& os, + basic_string_view<type_identity_t<Char>> format_str, basic_format_args<buffer_context<type_identity_t<Char>>> args) { - basic_memory_buffer<Char> buffer; + auto buffer = basic_memory_buffer<Char>(); detail::vformat_to(buffer, format_str, args); detail::write_buffer(os, buffer); } @@ -166,12 +195,19 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, fmt::print(cerr, "Don't {}!", "panic"); \endrst */ -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_MODULE_EXPORT template <typename... T> +void print(std::ostream& os, format_string<T...> fmt, T&&... args) { + vprint(os, fmt, fmt::make_format_args(args...)); } + +FMT_MODULE_EXPORT +template <typename... Args> +void print(std::wostream& os, + basic_format_string<wchar_t, type_identity_t<Args>...> fmt, + Args&&... args) { + vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); +} + FMT_END_NAMESPACE #endif // FMT_OSTREAM_H_ |