// Formatting library for C++ - formatters for standard library types // // Copyright (c) 2012 - present, Victor Zverovich // All rights reserved. // // For the license information refer to format.h. #ifndef FMT_STD_H_ #define FMT_STD_H_ #include "format.h" #include "ostream.h" #ifndef FMT_MODULE # include # include # include # include # include // std::reference_wrapper # include # include # include # include // std::type_info # include // std::make_index_sequence // Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC. # if FMT_CPLUSPLUS >= 201703L # if FMT_HAS_INCLUDE() && \ (!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0) # include # endif # if FMT_HAS_INCLUDE() # include # endif # if FMT_HAS_INCLUDE() # include # endif # endif // Use > instead of >= in the version check because may be // available after C++17 but before C++20 is marked as implemented. # if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE() # include # endif # if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE() # include # endif #endif // FMT_MODULE #if FMT_HAS_INCLUDE() # include #endif // GCC 4 does not support FMT_HAS_INCLUDE. #if FMT_HAS_INCLUDE() || defined(__GLIBCXX__) # include // Android NDK with gabi++ library on some architectures does not implement // abi::__cxa_demangle(). # ifndef __GABIXX_CXXABI_H__ # define FMT_HAS_ABI_CXA_DEMANGLE # endif #endif #ifdef FMT_CPP_LIB_FILESYSTEM // Use the provided definition. #elif defined(__cpp_lib_filesystem) # define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem #else # define FMT_CPP_LIB_FILESYSTEM 0 #endif #ifdef FMT_CPP_LIB_VARIANT // Use the provided definition. #elif defined(__cpp_lib_variant) # define FMT_CPP_LIB_VARIANT __cpp_lib_variant #else # define FMT_CPP_LIB_VARIANT 0 #endif FMT_BEGIN_NAMESPACE namespace detail { #if FMT_CPP_LIB_FILESYSTEM template auto get_path_string(const std::filesystem::path& p, const std::basic_string& native) { if constexpr (std::is_same_v && std::is_same_v) return to_utf8(native, to_utf8_error_policy::replace); else return p.string(); } template void write_escaped_path(basic_memory_buffer& quoted, const std::filesystem::path& p, const std::basic_string& native) { if constexpr (std::is_same_v && std::is_same_v) { auto buf = basic_memory_buffer(); write_escaped_string(std::back_inserter(buf), native); bool valid = to_utf8::convert(quoted, {buf.data(), buf.size()}); FMT_ASSERT(valid, "invalid utf16"); } else if constexpr (std::is_same_v) { write_escaped_string( std::back_inserter(quoted), native); } else { write_escaped_string(std::back_inserter(quoted), p.string()); } } #endif // FMT_CPP_LIB_FILESYSTEM #if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT template auto write_escaped_alternative(OutputIt out, const T& v, FormatContext& ctx) -> OutputIt { if constexpr (has_to_string_view::value) return write_escaped_string(out, detail::to_string_view(v)); if constexpr (std::is_same_v) return write_escaped_char(out, v); formatter, Char> underlying; maybe_set_debug_format(underlying, true); return underlying.format(v, ctx); } #endif #if FMT_CPP_LIB_VARIANT template struct is_variant_like_ : std::false_type {}; template struct is_variant_like_> : std::true_type {}; template class is_variant_formattable { template static auto check(std::index_sequence) -> std::conjunction< is_formattable, Char>...>; public: static constexpr bool value = decltype(check( std::make_index_sequence::value>()))::value; }; #endif // FMT_CPP_LIB_VARIANT #if FMT_USE_RTTI inline auto normalize_libcxx_inline_namespaces(string_view demangled_name_view, char* begin) -> string_view { // Normalization of stdlib inline namespace names. // libc++ inline namespaces. // std::__1::* -> std::* // std::__1::__fs::* -> std::* // libstdc++ inline namespaces. // std::__cxx11::* -> std::* // std::filesystem::__cxx11::* -> std::filesystem::* if (demangled_name_view.starts_with("std::")) { char* to = begin + 5; // std:: for (const char *from = to, *end = begin + demangled_name_view.size(); from < end;) { // This is safe, because demangled_name is NUL-terminated. if (from[0] == '_' && from[1] == '_') { const char* next = from + 1; while (next < end && *next != ':') next++; if (next[0] == ':' && next[1] == ':') { from = next + 2; continue; } } *to++ = *from++; } demangled_name_view = {begin, detail::to_unsigned(to - begin)}; } return demangled_name_view; } template auto normalize_msvc_abi_name(string_view abi_name_view, OutputIt out) -> OutputIt { const string_view demangled_name(abi_name_view); for (size_t i = 0; i < demangled_name.size(); ++i) { auto sub = demangled_name; sub.remove_prefix(i); if (sub.starts_with("enum ")) { i += 4; continue; } if (sub.starts_with("class ") || sub.starts_with("union ")) { i += 5; continue; } if (sub.starts_with("struct ")) { i += 6; continue; } if (*sub.begin() != ' ') *out++ = *sub.begin(); } return out; } template auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt { # ifdef FMT_HAS_ABI_CXA_DEMANGLE int status = 0; size_t size = 0; std::unique_ptr demangled_name_ptr( abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &free); string_view demangled_name_view; if (demangled_name_ptr) { demangled_name_view = normalize_libcxx_inline_namespaces( demangled_name_ptr.get(), demangled_name_ptr.get()); } else { demangled_name_view = string_view(ti.name()); } return detail::write_bytes(out, demangled_name_view); # elif FMT_MSC_VERSION && defined(_MSVC_STL_UPDATE) return normalize_msvc_abi_name(ti.name(), out); # elif FMT_MSC_VERSION && defined(_LIBCPP_VERSION) const string_view demangled_name = ti.name(); std::string name_copy(demangled_name.size(), '\0'); // normalize_msvc_abi_name removes class, struct, union etc that MSVC has in // front of types name_copy.erase(normalize_msvc_abi_name(demangled_name, name_copy.begin()), name_copy.end()); // normalize_libcxx_inline_namespaces removes the inline __1, __2, etc // namespaces libc++ uses for ABI versioning On MSVC ABI + libc++ // environments, we need to eliminate both of them. const string_view normalized_name = normalize_libcxx_inline_namespaces(name_copy, name_copy.data()); return detail::write_bytes(out, normalized_name); # else return detail::write_bytes(out, string_view(ti.name())); # endif } #endif // FMT_USE_RTTI template struct has_flip : std::false_type {}; template struct has_flip().flip())>> : std::true_type {}; template struct is_bit_reference_like { static constexpr bool value = std::is_convertible::value && std::is_nothrow_assignable::value && has_flip::value; }; // Workaround for libc++ incompatibility with C++ standard. // According to the Standard, `bitset::operator[] const` returns bool. #if defined(_LIBCPP_VERSION) && !defined(FMT_IMPORT_STD) template struct is_bit_reference_like> { static constexpr bool value = true; }; #endif template struct has_format_as : std::false_type {}; template struct has_format_as()))>> : std::true_type {}; template struct has_format_as_member : std::false_type {}; template struct has_format_as_member< T, void_t::format_as(std::declval()))>> : std::true_type {}; } // namespace detail template auto ptr(const std::unique_ptr& p) -> const void* { return p.get(); } template auto ptr(const std::shared_ptr& p) -> const void* { return p.get(); } #if FMT_CPP_LIB_FILESYSTEM template struct formatter { private: format_specs specs_; detail::arg_ref width_ref_; bool debug_ = false; char path_type_ = 0; public: FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } FMT_CONSTEXPR auto parse(parse_context& ctx) { auto it = ctx.begin(), end = ctx.end(); if (it == end) return it; it = detail::parse_align(it, end, specs_); if (it == end) return it; Char c = *it; if ((c >= '0' && c <= '9') || c == '{') it = detail::parse_width(it, end, specs_, width_ref_, ctx); if (it != end && *it == '?') { debug_ = true; ++it; } if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++); return it; } template auto format(const std::filesystem::path& p, FormatContext& ctx) const { auto specs = specs_; auto path_string = !path_type_ ? p.native() : p.generic_string(); detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx); if (!debug_) { auto s = detail::get_path_string(p, path_string); return detail::write(ctx.out(), basic_string_view(s), specs); } auto quoted = basic_memory_buffer(); detail::write_escaped_path(quoted, p, path_string); return detail::write(ctx.out(), basic_string_view(quoted.data(), quoted.size()), specs); } }; class path : public std::filesystem::path { public: auto display_string() const -> std::string { const std::filesystem::path& base = *this; return fmt::format(FMT_STRING("{}"), base); } auto system_string() const -> std::string { return string(); } auto generic_display_string() const -> std::string { const std::filesystem::path& base = *this; return fmt::format(FMT_STRING("{:g}"), base); } auto generic_system_string() const -> std::string { return generic_string(); } }; #endif // FMT_CPP_LIB_FILESYSTEM template struct formatter, Char> : nested_formatter, Char> { private: // This is a functor because C++11 doesn't support generic lambdas. struct writer { const std::bitset& bs; template FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt { for (auto pos = N; pos > 0; --pos) out = detail::write(out, bs[pos - 1] ? Char('1') : Char('0')); return out; } }; public: template auto format(const std::bitset& bs, FormatContext& ctx) const -> decltype(ctx.out()) { return this->write_padded(ctx, writer{bs}); } }; template struct formatter : basic_ostream_formatter {}; #ifdef __cpp_lib_optional template struct formatter, Char, std::enable_if_t::value>> { private: formatter, Char> underlying_; static constexpr basic_string_view optional = detail::string_literal{}; static constexpr basic_string_view none = detail::string_literal{}; public: FMT_CONSTEXPR auto parse(parse_context& ctx) { detail::maybe_set_debug_format(underlying_, true); return underlying_.parse(ctx); } template auto format(const std::optional& opt, FormatContext& ctx) const -> decltype(ctx.out()) { if (!opt) return detail::write(ctx.out(), none); auto out = ctx.out(); out = detail::write(out, optional); ctx.advance_to(out); out = underlying_.format(*opt, ctx); return detail::write(out, ')'); } }; #endif // __cpp_lib_optional #ifdef __cpp_lib_expected template struct formatter, Char, std::enable_if_t<(std::is_void::value || is_formattable::value) && is_formattable::value>> { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } template auto format(const std::expected& value, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); if (value.has_value()) { out = detail::write(out, "expected("); if constexpr (!std::is_void::value) out = detail::write_escaped_alternative(out, *value, ctx); } else { out = detail::write(out, "unexpected("); out = detail::write_escaped_alternative(out, value.error(), ctx); } *out++ = ')'; return out; } }; #endif // __cpp_lib_expected #ifdef __cpp_lib_source_location template <> struct formatter { FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); } template auto format(const std::source_location& loc, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); out = detail::write(out, loc.file_name()); out = detail::write(out, ':'); out = detail::write(out, loc.line()); out = detail::write(out, ':'); out = detail::write(out, loc.column()); out = detail::write(out, ": "); out = detail::write(out, loc.function_name()); return out; } }; #endif #if FMT_CPP_LIB_VARIANT template struct is_variant_like { static constexpr bool value = detail::is_variant_like_::value; }; template struct formatter { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } template auto format(const std::monostate&, FormatContext& ctx) const -> decltype(ctx.out()) { return detail::write(ctx.out(), "monostate"); } }; template struct formatter, detail::is_variant_formattable>>> { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } template auto format(const Variant& value, FormatContext& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); out = detail::write(out, "variant("); FMT_TRY { std::visit( [&](const auto& v) { out = detail::write_escaped_alternative(out, v, ctx); }, value); } FMT_CATCH(const std::bad_variant_access&) { detail::write(out, "valueless by exception"); } *out++ = ')'; return out; } }; #endif // FMT_CPP_LIB_VARIANT template <> struct formatter { private: format_specs specs_; detail::arg_ref width_ref_; bool debug_ = false; public: FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { auto it = ctx.begin(), end = ctx.end(); if (it == end) return it; it = detail::parse_align(it, end, specs_); char c = *it; if (it != end && ((c >= '0' && c <= '9') || c == '{')) it = detail::parse_width(it, end, specs_, width_ref_, ctx); if (it != end && *it == '?') { debug_ = true; ++it; } if (it != end && *it == 's') { specs_.set_type(presentation_type::string); ++it; } return it; } template FMT_CONSTEXPR20 auto format(const std::error_code& ec, FormatContext& ctx) const -> decltype(ctx.out()) { auto specs = specs_; detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx); auto buf = memory_buffer(); if (specs_.type() == presentation_type::string) { buf.append(ec.message()); } else { buf.append(string_view(ec.category().name())); buf.push_back(':'); detail::write(appender(buf), ec.value()); } auto quoted = memory_buffer(); auto str = string_view(buf.data(), buf.size()); if (debug_) { detail::write_escaped_string(std::back_inserter(quoted), str); str = string_view(quoted.data(), quoted.size()); } return detail::write(ctx.out(), str, specs); } }; #if FMT_USE_RTTI template <> struct formatter { public: FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { return ctx.begin(); } template auto format(const std::type_info& ti, Context& ctx) const -> decltype(ctx.out()) { return detail::write_demangled_name(ctx.out(), ti); } }; #endif // FMT_USE_RTTI template struct formatter< T, char, typename std::enable_if::value>::type> { private: bool with_typename_ = false; public: FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { auto it = ctx.begin(); auto end = ctx.end(); if (it == end || *it == '}') return it; if (*it == 't') { ++it; with_typename_ = FMT_USE_RTTI != 0; } return it; } template auto format(const std::exception& ex, Context& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); #if FMT_USE_RTTI if (with_typename_) { out = detail::write_demangled_name(out, typeid(ex)); *out++ = ':'; *out++ = ' '; } #endif return detail::write_bytes(out, string_view(ex.what())); } }; // We can't use std::vector::reference and // std::bitset::reference because the compiler can't deduce Allocator and N // in partial specialization. template struct formatter::value>> : formatter { template FMT_CONSTEXPR auto format(const BitRef& v, FormatContext& ctx) const -> decltype(ctx.out()) { return formatter::format(v, ctx); } }; template struct formatter, Char, enable_if_t::value>> : formatter { template auto format(const std::atomic& v, FormatContext& ctx) const -> decltype(ctx.out()) { return formatter::format(v.load(), ctx); } }; #ifdef __cpp_lib_atomic_flag_test template struct formatter : formatter { template auto format(const std::atomic_flag& v, FormatContext& ctx) const -> decltype(ctx.out()) { return formatter::format(v.test(), ctx); } }; #endif // __cpp_lib_atomic_flag_test template struct formatter, Char> { private: detail::dynamic_format_specs specs_; template FMT_CONSTEXPR auto do_format(const std::complex& c, detail::dynamic_format_specs& specs, FormatContext& ctx, OutputIt out) const -> OutputIt { if (c.real() != 0) { *out++ = Char('('); out = detail::write(out, c.real(), specs, ctx.locale()); specs.set_sign(sign::plus); out = detail::write(out, c.imag(), specs, ctx.locale()); if (!detail::isfinite(c.imag())) *out++ = Char(' '); *out++ = Char('i'); *out++ = Char(')'); return out; } out = detail::write(out, c.imag(), specs, ctx.locale()); if (!detail::isfinite(c.imag())) *out++ = Char(' '); *out++ = Char('i'); return out; } public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, detail::type_constant::value); } template auto format(const std::complex& c, FormatContext& ctx) const -> decltype(ctx.out()) { auto specs = specs_; if (specs.dynamic()) { detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, specs.width_ref, ctx); detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision, specs.precision_ref, ctx); } if (specs.width == 0) return do_format(c, specs, ctx, ctx.out()); auto buf = basic_memory_buffer(); auto outer_specs = format_specs(); outer_specs.width = specs.width; outer_specs.copy_fill_from(specs); outer_specs.set_align(specs.align()); specs.width = 0; specs.set_fill({}); specs.set_align(align::none); do_format(c, specs, ctx, basic_appender(buf)); return detail::write(ctx.out(), basic_string_view(buf.data(), buf.size()), outer_specs); } }; template struct formatter, Char, // Guard against format_as because reference_wrapper is // implicitly convertible to T&. enable_if_t, Char>::value && !detail::has_format_as::value && !detail::has_format_as_member::value>> : formatter, Char> { template auto format(std::reference_wrapper ref, FormatContext& ctx) const -> decltype(ctx.out()) { return formatter, Char>::format(ref.get(), ctx); } }; FMT_END_NAMESPACE #endif // FMT_STD_H_