From 3ba09eff8c25bb54b2478f5479d9c38d9c26b942 Mon Sep 17 00:00:00 2001 From: LocalSpook Date: Tue, 11 Mar 2025 20:28:11 -0700 Subject: [PATCH] Add support for incomplete types --- include/fmt/base.h | 9 +++++--- include/fmt/color.h | 4 +++- include/fmt/format.h | 6 +++++- include/fmt/ranges.h | 18 ++++++++++++++-- test/CMakeLists.txt | 1 + test/incomplete-type-test.cc | 41 ++++++++++++++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 test/incomplete-type-test.cc 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/CMakeLists.txt b/test/CMakeLists.txt index e1ea260d9adc..64d658ec332b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -53,6 +53,7 @@ add_fmt_test(assert-test) add_fmt_test(chrono-test) add_fmt_test(color-test) add_fmt_test(gtest-extra-test) +add_fmt_test(incomplete-type-test) add_fmt_test(format-test mock-allocator.h) if (MSVC) target_compile_options(format-test PRIVATE /bigobj) diff --git a/test/incomplete-type-test.cc b/test/incomplete-type-test.cc new file mode 100644 index 000000000000..1607711351fc --- /dev/null +++ b/test/incomplete-type-test.cc @@ -0,0 +1,41 @@ +// Formatting library for C++ - formatting library tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/format.h" +#include "fmt/color.h" +#include "gtest/gtest.h" + +// Only defined after all the tests. +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)); + EXPECT_EQ(fmt::format("{:4}", fmt::styled(external_instance, fg(fmt::color::red))), fmt::format("{:4}", fmt::styled(42, fg(fmt::color::red)))); +} + +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); +}