Skip to content

Commit db7e2c4

Browse files
localspookvitaut
authored andcommitted
Add support for incomplete types
1 parent db40595 commit db7e2c4

File tree

4 files changed

+54
-4
lines changed

4 files changed

+54
-4
lines changed

include/fmt/base.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1033,6 +1033,10 @@ enum {
10331033

10341034
struct view {};
10351035

1036+
template <typename T, bool = true> struct is_view : std::false_type {};
1037+
template <typename T>
1038+
struct is_view<T, sizeof(T) != 0> : std::is_base_of<view, T> {};
1039+
10361040
template <typename Char, typename T> struct named_arg;
10371041
template <typename T> struct is_named_arg : std::false_type {};
10381042
template <typename T> struct is_static_named_arg : std::false_type {};
@@ -2726,7 +2730,7 @@ template <typename... T> struct fstring {
27262730
template <size_t N>
27272731
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
27282732
using namespace detail;
2729-
static_assert(count<(std::is_base_of<view, remove_reference_t<T>>::value &&
2733+
static_assert(count<(is_view<remove_cvref_t<T>>::value &&
27302734
std::is_reference<T>::value)...>() == 0,
27312735
"passing views as lvalues is disallowed");
27322736
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));

include/fmt/color.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,12 +468,14 @@ template <typename Char> inline void reset_color(buffer<Char>& buffer) {
468468
buffer.append(reset_color.begin(), reset_color.end());
469469
}
470470

471-
template <typename T> struct styled_arg : view {
471+
template <typename T> struct styled_arg {
472472
const T& value;
473473
text_style style;
474474
styled_arg(const T& v, text_style s) : value(v), style(s) {}
475475
};
476476

477+
template <typename T> struct is_view<styled_arg<T>> : std::true_type {};
478+
477479
template <typename Char>
478480
void vformat_to(buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt,
479481
basic_format_args<buffered_context<Char>> args) {

include/fmt/ranges.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ struct formatter<
620620
};
621621

622622
template <typename It, typename Sentinel, typename Char = char>
623-
struct join_view : detail::view {
623+
struct join_view {
624624
It begin;
625625
Sentinel end;
626626
basic_string_view<Char> sep;
@@ -629,6 +629,13 @@ struct join_view : detail::view {
629629
: begin(std::move(b)), end(e), sep(s) {}
630630
};
631631

632+
namespace detail {
633+
634+
template <typename It, typename Sentinel, typename Char>
635+
struct is_view<join_view<It, Sentinel, Char>> : std::true_type {};
636+
637+
} // namespace detail
638+
632639
template <typename It, typename Sentinel, typename Char>
633640
struct formatter<join_view<It, Sentinel, Char>, Char> {
634641
private:
@@ -670,14 +677,21 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
670677
}
671678
};
672679

673-
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
680+
template <typename Char, typename Tuple> struct tuple_join_view {
674681
const Tuple& tuple;
675682
basic_string_view<Char> sep;
676683

677684
tuple_join_view(const Tuple& t, basic_string_view<Char> s)
678685
: tuple(t), sep{s} {}
679686
};
680687

688+
namespace detail {
689+
690+
template <typename Char, typename Tuple>
691+
struct is_view<tuple_join_view<Char, Tuple>> : std::true_type {};
692+
693+
} // namespace detail
694+
681695
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
682696
// support in tuple_join. It is disabled by default because of issues with
683697
// the dynamic width and precision.

test/format-test.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,3 +2540,33 @@ TEST(base_test, format_byte) {
25402540
EXPECT_EQ(s, "42");
25412541
}
25422542
#endif
2543+
2544+
// Only defined after the test case.
2545+
struct incomplete_type;
2546+
extern const incomplete_type& external_instance;
2547+
2548+
FMT_BEGIN_NAMESPACE
2549+
2550+
template <> struct formatter<incomplete_type> : formatter<int> {
2551+
auto format(const incomplete_type& x, context& ctx) const
2552+
-> decltype(ctx.out());
2553+
};
2554+
2555+
FMT_END_NAMESPACE
2556+
2557+
TEST(incomplete_type_test, format) {
2558+
EXPECT_EQ(fmt::format("{}", external_instance), fmt::format("{}", 42));
2559+
EXPECT_EQ(fmt::format("{:4}", external_instance), fmt::format("{:4}", 42));
2560+
}
2561+
2562+
struct incomplete_type {
2563+
int i;
2564+
};
2565+
2566+
const incomplete_type& external_instance{42};
2567+
2568+
auto fmt::formatter<incomplete_type>::format(const incomplete_type& x,
2569+
fmt::context& ctx) const
2570+
-> decltype(ctx.out()) {
2571+
return fmt::formatter<int>::format(x.i, ctx);
2572+
}

0 commit comments

Comments
 (0)