llvm.org GIT mirror llvm / 2ef2a88
Simplify format member detection in FormatVariadic Summary: This replaces the format member search, which was quite complicated, with a more direct approach to detecting whether a class should be formatted using the format-member method. Instead we use a special type llvm::format_adapter, which every adapter must inherit from. Then the search can be simply implemented with the is_base_of type trait. Aside from the simplification, I like this way more because it makes it more explicit that you are supposed to use this type only for adapter-like formattings, and the other approach (format_provider overloads) should be used as a default (a mistake I made when first trying to use this library). The only slight change in behaviour here is that now choose the format-adapter branch even if the format member invocation will fail to compile (e.g. because it is a non-const member function and we are passing a const adapter), whereas previously we would have gone on to search for format_providers for the type. However, I think that is actually a good thing, as it probably means the programmer did something wrong. Reviewers: zturner, inglorion Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D27679 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@289795 91177308-0d34-0410-b5e6-96231b3b80d8 Pavel Labath 3 years ago
7 changed file(s) with 84 addition(s) and 162 deletion(s). Raw diff Collapse all Expand all
330330 to extend the mechanism for formatting a type that the library already knows how to
331331 format. For that, we need something else.
332332
333 2. Provide a **format adapter** with a non-static format method.
333 2. Provide a **format adapter** inheriting from ``llvm::FormatAdapter``.
334334
335335 .. code-block:: c++
336336
337337 namespace anything {
338 struct format_int_custom {
339 int N;
340 explicit format_int_custom(int N) : N(N) {}
341 void format(llvm::raw_ostream &Stream, StringRef Style) {
342 // Do whatever is necessary to format ``N`` into ``Stream``
338 struct format_int_custom : public llvm::FormatAdapter {
339 explicit format_int_custom(int N) : llvm::FormatAdapter(N) {}
340 void format(llvm::raw_ostream &Stream, StringRef Style) override {
341 // Do whatever is necessary to format ``this->Item`` into ``Stream``
343342 }
344343 };
345344 }
349348 }
350349 }
351350
352 If the search for a specialization of ``format_provider`` for the given type
353 fails, ``formatv`` will subsequently check the argument for an instance method
354 named ``format`` with the signature described above. If so, it will call the
351 If the type is detected to be derived from ``FormatAdapter``, ``formatv``
352 will call the
355353 ``format`` method on the argument passing in the specified style. This allows
356354 one to provide custom formatting of any type, including one which already has
357355 a builtin format provider.
1616 #include "llvm/Support/raw_ostream.h"
1717
1818 namespace llvm {
19 template class AdapterBase {
19 template class FormatAdapter : public detail::format_adapter {
2020 protected:
21 explicit AdapterBase(T &&Item) : Item(Item) {}
21 explicit FormatAdapter(T &&Item) : Item(Item) {}
2222
2323 T Item;
24
2425 static_assert(!detail::uses_missing_provider::value,
2526 "Item does not have a format provider!");
2627 };
2728
2829 namespace detail {
29 template class AlignAdapter : public AdapterBase {
30 template class AlignAdapter final : public FormatAdapter {
3031 AlignStyle Where;
3132 size_t Amount;
3233
3334 public:
3435 AlignAdapter(T &&Item, AlignStyle Where, size_t Amount)
35 : AdapterBase(std::forward(Item)), Where(Where), Amount(Amount) {}
36 : FormatAdapter(std::forward(Item)), Where(Where), Amount(Amount) {}
3637
3738 void format(llvm::raw_ostream &Stream, StringRef Style) {
38 auto Wrapper = detail::build_format_wrapper(std::forward(this->Item));
39 FmtAlign(Wrapper, Where, Amount).format(Stream, Style);
39 auto Adapter = detail::build_format_adapter(std::forward(this->Item));
40 FmtAlign(Adapter, Where, Amount).format(Stream, Style);
4041 }
4142 };
4243
43 template class PadAdapter : public AdapterBase {
44 template class PadAdapter final : public FormatAdapter {
4445 size_t Left;
4546 size_t Right;
4647
4748 public:
4849 PadAdapter(T &&Item, size_t Left, size_t Right)
49 : AdapterBase(std::forward(Item)), Left(Left), Right(Right) {}
50 : FormatAdapter(std::forward(Item)), Left(Left), Right(Right) {}
5051
5152 void format(llvm::raw_ostream &Stream, StringRef Style) {
52 auto Wrapper = detail::build_format_wrapper(std::forward(this->Item));
53 auto Adapter = detail::build_format_adapter(std::forward(this->Item));
5354 Stream.indent(Left);
54 Wrapper.format(Stream, Style);
55 Adapter.format(Stream, Style);
5556 Stream.indent(Right);
5657 }
5758 };
5859
59 template class RepeatAdapter : public AdapterBase {
60 template class RepeatAdapter final : public FormatAdapter {
6061 size_t Count;
6162
6263 public:
6364 RepeatAdapter(T &&Item, size_t Count)
64 : AdapterBase(std::forward(Item)), Count(Count) {}
65 : FormatAdapter(std::forward(Item)), Count(Count) {}
6566
6667 void format(llvm::raw_ostream &Stream, StringRef Style) {
67 auto Wrapper = detail::build_format_wrapper(std::forward(this->Item));
68 auto Adapter = detail::build_format_adapter(std::forward(this->Item));
6869 for (size_t I = 0; I < Count; ++I) {
69 Wrapper.format(Stream, Style);
70 Adapter.format(Stream, Style);
7071 }
7172 }
7273 };
8889 }
8990 }
9091
91 #endif
92 #endif
1717 enum class AlignStyle { Left, Center, Right };
1818
1919 struct FmtAlign {
20 detail::format_wrapper &Wrapper;
20 detail::format_adapter &Adapter;
2121 AlignStyle Where;
2222 size_t Amount;
2323
24 FmtAlign(detail::format_wrapper &Wrapper, AlignStyle Where, size_t Amount)
25 : Wrapper(Wrapper), Where(Where), Amount(Amount) {}
24 FmtAlign(detail::format_adapter &Adapter, AlignStyle Where, size_t Amount)
25 : Adapter(Adapter), Where(Where), Amount(Amount) {}
2626
2727 void format(raw_ostream &S, StringRef Options) {
2828 // If we don't need to align, we can format straight into the underlying
3131 // TODO: Make the format method return the number of bytes written, that
3232 // way we can also skip the intermediate stream for left-aligned output.
3333 if (Amount == 0) {
34 Wrapper.format(S, Options);
34 Adapter.format(S, Options);
3535 return;
3636 }
3737 SmallString<64> Item;
3838 raw_svector_ostream Stream(Item);
3939
40 Wrapper.format(Stream, Options);
40 Adapter.format(Stream, Options);
4141 if (Amount <= Item.size()) {
4242 S << Item;
4343 return;
6565 };
6666 }
6767
68 #endif
68 #endif
393393 auto Begin = V.begin();
394394 auto End = V.end();
395395 if (Begin != End) {
396 auto Wrapper =
397 detail::build_format_wrapper(std::forward(*Begin));
398 Wrapper.format(Stream, ArgStyle);
396 auto Adapter =
397 detail::build_format_adapter(std::forward(*Begin));
398 Adapter.format(Stream, ArgStyle);
399399 ++Begin;
400400 }
401401 while (Begin != End) {
402402 Stream << Sep;
403 auto Wrapper =
404 detail::build_format_wrapper(std::forward(*Begin));
405 Wrapper.format(Stream, ArgStyle);
403 auto Adapter =
404 detail::build_format_adapter(std::forward(*Begin));
405 Adapter.format(Stream, ArgStyle);
406406 ++Begin;
407407 }
408408 }
7070 // parameters in a template class that derives from a non-template superclass.
7171 // Essentially, we are converting a std::tuple> to a
7272 // std::vector.
73 struct create_wrappers {
73 struct create_adapters {
7474 template
75 std::vector operator()(Ts &... Items) {
76 return std::vector{&Items...};
75 std::vector operator()(Ts &... Items) {
76 return std::vector{&Items...};
7777 }
7878 };
7979
8080 StringRef Fmt;
81 std::vectorwrapper *> Wrappers;
81 std::vectoradapter *> Adapters;
8282 std::vector Replacements;
8383
8484 static bool consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
9090 public:
9191 formatv_object_base(StringRef Fmt, std::size_t ParamCount)
9292 : Fmt(Fmt), Replacements(parseFormatString(Fmt)) {
93 Wrappers.reserve(ParamCount);
93 Adapters.reserve(ParamCount);
9494 }
9595
9696 void format(raw_ostream &S) const {
101101 S << R.Spec;
102102 continue;
103103 }
104 if (R.Index >= Wrappers.size()) {
104 if (R.Index >= Adapters.size()) {
105105 S << R.Spec;
106106 continue;
107107 }
108108
109 auto W = Wrappers[R.Index];
109 auto W = Adapters[R.Index];
110110
111111 FmtAlign Align(*W, R.Where, R.Align);
112112 Align.format(S, R.Options);
137137 };
138138
139139 template class formatv_object : public formatv_object_base {
140 // Storage for the parameter wrappers. Since the base class erases the type
140 // Storage for the parameter adapters. Since the base class erases the type
141141 // of the parameters, we have to own the storage for the parameters here, and
142142 // have the base class store type-erased pointers into this tuple.
143143 Tuple Parameters;
146146 formatv_object(StringRef Fmt, Tuple &&Params)
147147 : formatv_object_base(Fmt, std::tuple_size::value),
148148 Parameters(std::move(Params)) {
149 Wrappers = apply_tuple(create_wrappers(), Parameters);
149 Adapters = apply_tuple(create_adapters(), Parameters);
150150 }
151151 };
152152
233233 //
234234 template
235235 inline auto formatv(const char *Fmt, Ts &&... Vals) -> formatv_object
236 std::make_tuple(detail::build_format_wrapper(std::forward(Vals))...))> {
236 std::make_tuple(detail::build_format_adapter(std::forward(Vals))...))> {
237237 using ParamTuple = decltype(
238 std::make_tuple(detail::build_format_wrapper(std::forward(Vals))...));
238 std::make_tuple(detail::build_format_adapter(std::forward(Vals))...));
239239 return formatv_object(
240240 Fmt,
241 std::make_tuple(detail::build_format_wrapper(std::forward(Vals))...));
241 std::make_tuple(detail::build_format_adapter(std::forward(Vals))...));
242242 }
243243
244244 } // end namespace llvm
1818 template struct format_provider {};
1919
2020 namespace detail {
21
22 class format_wrapper {
21 class format_adapter {
2322 protected:
24 virtual ~format_wrapper() {}
23 virtual ~format_adapter() {}
2524
2625 public:
27 virtual void format(llvm::raw_ostream &S, StringRef Options) = 0;
26 virtual void format(raw_ostream &S, StringRef Options) = 0;
2827 };
2928
30 template class member_format_wrapper : public format_wrapper {
29 template class provider_format_adapter : public format_adapter {
3130 T Item;
3231
3332 public:
34 explicit member_format_wrapper(T &&Item) : Item(Item) {}
35
36 void format(llvm::raw_ostream &S, StringRef Options) override {
37 Item.format(S, Options);
38 }
39 };
40
41 template class provider_format_wrapper : public format_wrapper {
42 T Item;
43
44 public:
45 explicit provider_format_wrapper(T &&Item) : Item(Item) {}
33 explicit provider_format_adapter(T &&Item) : Item(Item) {}
4634
4735 void format(llvm::raw_ostream &S, StringRef Options) override {
4836 format_provider::type>::format(Item, S, Options);
4937 }
5038 };
5139
52 template class missing_format_wrapper;
53
54 // Test if T is a class that contains a member function with the signature:
55 //
56 // void format(raw_ostream &, StringRef);
57 //
58 // It is assumed T is a non-reference type.
59 template class has_FormatMember {
60 public:
61 static bool const value = false;
62 };
63
64 template
65 class has_FormatMember
66 typename std::enable_if::value &&
67 std::is_const::value>::type> {
68 using CleanT = typename std::remove_volatile::type;
69 using Signature_format = void (CleanT::*)(llvm::raw_ostream &S,
70 StringRef Options) const;
71
72 template
73 static char test2(SameType *);
74
75 template static double test2(...);
76
77 public:
78 static bool const value = (sizeof(test2(nullptr)) == 1);
79 };
80
81 template
82 class has_FormatMember<
83 T, typename std::enable_if::value &&
84 !std::is_const::value>::type> {
85 using CleanT = typename std::remove_cv::type;
86 using Signature_format = void (CleanT::*)(llvm::raw_ostream &S,
87 StringRef Options);
88
89 template
90 static char test2(SameType *);
91
92 template static double test2(...);
93
94 public:
95 static bool const value =
96 (sizeof(test2(nullptr)) == 1) || has_FormatMember::value;
97 };
40 template class missing_format_adapter;
9841
9942 // Test if format_provider is defined on T and contains a member function
10043 // with the signature:
12164 struct uses_format_member
12265 : public std::integral_constant<
12366 bool,
124 has_FormatMember::type>::value> {};
67 std::is_base_of
68 typename std::remove_reference::type>::value> {};
12569
12670 // Simple template that decides whether a type T should use the format_provider
12771 // based format() invocation. The member function takes priority, so this test
14387 !uses_format_provider::value> {};
14488
14589 template
146 typename std::enable_if::value,
147 member_format_wrapper>::type
148 build_format_wrapper(T &&Item) {
149 return member_format_wrapper(std::forward(Item));
90 typename std::enable_if::value, T>::type
91 build_format_adapter(T &&Item) {
92 return std::forward(Item);
15093 }
15194
15295 template
15396 typename std::enable_if::value,
154 provider_format_wrapper>::type
155 build_format_wrapper(T &&Item) {
156 return provider_format_wrapper(std::forward(Item));
97 provider_format_adapter>::type
98 build_format_adapter(T &&Item) {
99 return provider_format_adapter(std::forward(Item));
157100 }
158101
159102 template
160103 typename std::enable_if::value,
161 missing_format_wrapper>::type
162 build_format_wrapper(T &&Item) {
163 return missing_format_wrapper();
104 missing_format_adapter>::type
105 build_format_adapter(T &&Item) {
106 return missing_format_adapter();
164107 }
165108 }
166109 }
1212
1313 using namespace llvm;
1414
15 // Compile-time tests for the uses_format_member template
15 // Compile-time tests templates in the detail namespace.
1616 namespace {
17 struct ConstFormat {
18 void format(raw_ostream &OS, StringRef Opt) const { OS << "ConstFormat"; }
17 struct Format : public FormatAdapter {
18 Format(int N) : FormatAdapter(std::move(N)) {}
19 void format(raw_ostream &OS, StringRef Opt) override { OS << "Format"; }
1920 };
2021
21 struct Format {
22 void format(raw_ostream &OS, StringRef Opt) { OS << "Format"; }
23 };
24
2522 using detail::uses_format_member;
23 using detail::uses_missing_provider;
2624
2725 static_assert(uses_format_member::value, "");
2826 static_assert(uses_format_member::value, "");
2927 static_assert(uses_format_member::value, "");
30 static_assert(!uses_format_member::value, "");
31 static_assert(!uses_format_member::value, "");
32 static_assert(!uses_format_member::value, "");
33 static_assert(!uses_format_member::value, "");
34
35 static_assert(uses_format_member::value, "");
36 static_assert(uses_format_member::value, "");
37 static_assert(uses_format_member::value, "");
38 static_assert(uses_format_member::value, "");
39 static_assert(uses_format_member::value, "");
40 static_assert(uses_format_member::value, "");
41 static_assert(uses_format_membervolatile ConstFormat &>::value, "");
28 static_assert(uses_format_memberFormat>::value, "");
29 static_assert(uses_format_member::value, "");
30 static_assert(uses_format_member::value, "");
31 static_assert(uses_format_member::value, "");
32
33 struct NoFormat {};
34 static_assert(uses_missing_provider::value, "");
4235 }
4336
4437 TEST(FormatVariadicTest, EmptyFormatString) {
534527 }
535528
536529 TEST(FormatVariadicTest, Adapter) {
537 class Negative {
538 int N;
539
530 class Negative : public FormatAdapter {
540531 public:
541 explicit Negative(int N) : N(N) {}
542 void format(raw_ostream &S, StringRef Options) const { S << -N; }
532 explicit Negative(int N) : FormatAdapter(std::move(N)) {}
533 void format(raw_ostream &S, StringRef Options) override { S << -Item; }
543534 };
544535
545536 EXPECT_EQ("-7", formatv("{0}", Negative(7)).str());
565556 EXPECT_EQ("1 2", S2);
566557 }
567558
568 TEST(FormatVariadicTest, FormatMember) {
569 EXPECT_EQ("Format", formatv("{0}", Format()).str());
570
571 Format var;
559 TEST(FormatVariadicTest, FormatAdapter) {
560 EXPECT_EQ("Format", formatv("{0}", Format(1)).str());
561
562 Format var(1);
572563 EXPECT_EQ("Format", formatv("{0}", var).str());
573564 EXPECT_EQ("Format", formatv("{0}", std::move(var)).str());
574565
575566 // Not supposed to compile
576 // const Format cvar{};
567 // const Format cvar(1);
577568 // EXPECT_EQ("Format", formatv("{0}", cvar).str());
578569 }
579
580 TEST(FormatVariadicTest, FormatMemberConst) {
581 EXPECT_EQ("ConstFormat", formatv("{0}", ConstFormat()).str());
582
583 ConstFormat var;
584 EXPECT_EQ("ConstFormat", formatv("{0}", var).str());
585 EXPECT_EQ("ConstFormat", formatv("{0}", std::move(var)).str());
586
587 const ConstFormat cvar{};
588 EXPECT_EQ("ConstFormat", formatv("{0}", cvar).str());
589 }