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 <typename T> struct is_view : std::false_type {}; template <typename Char, typename T> struct named_arg; template <typename T> struct is_named_arg : std::false_type {}; @@ -1039,7 +1039,7 @@ template <typename T> struct is_static_named_arg : std::false_type {}; template <typename Char, typename T> struct is_named_arg<named_arg<Char, T>> : std::true_type {}; -template <typename Char, typename T> struct named_arg : view { +template <typename Char, typename T> struct named_arg { const Char* name; const T& value; @@ -1047,6 +1047,9 @@ template <typename Char, typename T> struct named_arg : view { static_assert(!is_named_arg<T>::value, "nested named arguments"); }; +template <typename Char, typename T> +struct is_view<named_arg<Char, T>> : std::true_type {}; + template <bool B = false> constexpr auto count() -> int { return B ? 1 : 0; } template <bool B1, bool B2, bool... Tail> constexpr auto count() -> int { return (B1 ? 1 : 0) + count<B2, Tail...>(); @@ -2715,7 +2718,7 @@ template <typename... T> struct fstring { template <size_t N> FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) { using namespace detail; - static_assert(count<(std::is_base_of<view, remove_reference_t<T>>::value && + static_assert(count<(is_view<remove_cvref_t<T>>::value && std::is_reference<T>::value)...>() == 0, "passing views as lvalues is disallowed"); if (FMT_USE_CONSTEVAL) parse_format_string<char>(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 <typename Char> inline void reset_color(buffer<Char>& buffer) { buffer.append(reset_color.begin(), reset_color.end()); } -template <typename T> struct styled_arg : view { +template <typename T> struct styled_arg { const T& value; text_style style; styled_arg(const T& v, text_style s) : value(v), style(s) {} }; +template <typename T> struct is_view<styled_arg<T>> : std::true_type {}; + template <typename Char> void vformat_to(buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt, basic_format_args<buffered_context<Char>> 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 <typename T, typename Char, size_t N, fmt::detail::fixed_string<Char, N> 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 <typename T, typename Char, size_t N, + fmt::detail::fixed_string<Char, N> Str> +struct is_view<static_named_arg<T, Char, N, Str>> : std::true_type {}; + template <typename T, typename Char, size_t N, fmt::detail::fixed_string<Char, N> Str> struct is_named_arg<static_named_arg<T, Char, N, Str>> : 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 <typename It, typename Sentinel, typename Char = char> -struct join_view : detail::view { +struct join_view { It begin; Sentinel end; basic_string_view<Char> sep; @@ -629,6 +629,13 @@ struct join_view : detail::view { : begin(std::move(b)), end(e), sep(s) {} }; +namespace detail { + +template <typename It, typename Sentinel, typename Char> +struct is_view<join_view<It, Sentinel, Char>> : std::true_type {}; + +} // namespace detail + template <typename It, typename Sentinel, typename Char> struct formatter<join_view<It, Sentinel, Char>, Char> { private: @@ -670,7 +677,7 @@ struct formatter<join_view<It, Sentinel, Char>, Char> { } }; -template <typename Char, typename Tuple> struct tuple_join_view : detail::view { +template <typename Char, typename Tuple> struct tuple_join_view { const Tuple& tuple; basic_string_view<Char> sep; @@ -678,6 +685,13 @@ template <typename Char, typename Tuple> struct tuple_join_view : detail::view { : tuple(t), sep{s} {} }; +namespace detail { + +template <typename Char, typename Tuple> +struct is_view<tuple_join_view<Char, Tuple>> : 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<incomplete_type> : formatter<int> { + 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<incomplete_type>::format(const incomplete_type& x, + fmt::context& ctx) const + -> decltype(ctx.out()) { + return fmt::formatter<int>::format(x.i, ctx); +}