diff --git a/include/fmt/base.h b/include/fmt/base.h index fe73e37795aa..79ec762c3764 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -1030,7 +1030,7 @@ enum { pointer_set = set(type::pointer_type) }; -struct view {}; +template struct is_view : std::false_type {}; template struct named_arg; template struct is_named_arg : std::false_type {}; @@ -1039,7 +1039,7 @@ template struct is_static_named_arg : std::false_type {}; template struct is_named_arg> : std::true_type {}; -template struct named_arg : view { +template struct named_arg { const Char* name; const T& value; @@ -1047,6 +1047,9 @@ template struct named_arg : view { static_assert(!is_named_arg::value, "nested named arguments"); }; +template +struct is_view> : std::true_type {}; + template constexpr auto count() -> int { return B ? 1 : 0; } template constexpr auto count() -> int { return (B1 ? 1 : 0) + count(); @@ -2715,7 +2718,7 @@ template struct fstring { template FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) { using namespace detail; - static_assert(count<(std::is_base_of>::value && + static_assert(count<(is_view>::value && std::is_reference::value)...>() == 0, "passing views as lvalues is disallowed"); if (FMT_USE_CONSTEVAL) parse_format_string(s, checker(s, arg_pack())); diff --git a/include/fmt/color.h b/include/fmt/color.h index 638f15b43f38..ab431eaa6e3f 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -468,12 +468,14 @@ template inline void reset_color(buffer& buffer) { buffer.append(reset_color.begin(), reset_color.end()); } -template struct styled_arg : view { +template struct styled_arg { const T& value; text_style style; styled_arg(const T& v, text_style s) : value(v), style(s) {} }; +template struct is_view> : std::true_type {}; + template void vformat_to(buffer& buf, text_style ts, basic_string_view fmt, basic_format_args> args) { diff --git a/include/fmt/format.h b/include/fmt/format.h index 287e71631e35..5d2d826eb8f8 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3554,13 +3554,17 @@ FMT_CONSTEXPR void handle_dynamic_spec( #if FMT_USE_NONTYPE_TEMPLATE_ARGS template Str> -struct static_named_arg : view { +struct static_named_arg { static constexpr auto name = Str.data; const T& value; static_named_arg(const T& v) : value(v) {} }; +template Str> +struct is_view> : std::true_type {}; + template Str> struct is_named_arg> : std::true_type {}; diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 77d645f8e114..5a0c7b17aa2f 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -620,7 +620,7 @@ struct formatter< }; template -struct join_view : detail::view { +struct join_view { It begin; Sentinel end; basic_string_view sep; @@ -629,6 +629,13 @@ struct join_view : detail::view { : begin(std::move(b)), end(e), sep(s) {} }; +namespace detail { + +template +struct is_view> : std::true_type {}; + +} // namespace detail + template struct formatter, Char> { private: @@ -670,7 +677,7 @@ struct formatter, Char> { } }; -template struct tuple_join_view : detail::view { +template struct tuple_join_view { const Tuple& tuple; basic_string_view sep; @@ -678,6 +685,13 @@ template struct tuple_join_view : detail::view { : tuple(t), sep{s} {} }; +namespace detail { + +template +struct is_view> : std::true_type {}; + +} // namespace detail + // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers // support in tuple_join. It is disabled by default because of issues with // the dynamic width and precision. diff --git a/test/format-test.cc b/test/format-test.cc index f83d16260e45..fdd5a1a77724 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2586,3 +2586,33 @@ TEST(base_test, format_byte) { EXPECT_EQ(s, "42"); } #endif + +// Only defined after the test case. +struct incomplete_type; +extern const incomplete_type& external_instance; + +FMT_BEGIN_NAMESPACE + +template <> struct formatter : formatter { + auto format(const incomplete_type& x, context& ctx) const + -> decltype(ctx.out()); +}; + +FMT_END_NAMESPACE + +TEST(incomplete_type_test, format) { + EXPECT_EQ(fmt::format("{}", external_instance), fmt::format("{}", 42)); + EXPECT_EQ(fmt::format("{:4}", external_instance), fmt::format("{:4}", 42)); +} + +struct incomplete_type { + int i; +}; + +const incomplete_type& external_instance{42}; + +auto fmt::formatter::format(const incomplete_type& x, + fmt::context& ctx) const + -> decltype(ctx.out()) { + return fmt::formatter::format(x.i, ctx); +}