@@ -42,6 +42,27 @@ struct enable_if_valid {
42
42
};
43
43
template <typename Check, typename T> using enable_if_valid_t = typename enable_if_valid<Check, T>::type;
44
44
45
+
46
+ namespace detail {
47
+ // Returns a value of the given type. No implementation, so this is
48
+ // meant for use in decltype only. Identical to std::declval.
49
+ template <typename T> T declval ();
50
+
51
+ // Function that accept a pointer of the given type. Can be used to
52
+ // detect if another type is convertible to T.
53
+ template <typename T>
54
+ void accepts_pointer_of_type (const T*);
55
+
56
+ // Simplified and integrated version of std::enable_if_t and
57
+ // std::is_base_of (which are not available on AVR). Integrating them
58
+ // makes things more specific and thus simpler.
59
+ // TODO: This check does not detect private base classes. To do so, a
60
+ // more complicated check is needed, something like
61
+ // https://stackoverflow.com/questions/2910979/how-does-is-base-of-work
62
+ template <typename Base, typename Derived>
63
+ using enable_if_base_of = decltype(accepts_pointer_of_type<Base>(declval<Derived*>()));
64
+ }
65
+
45
66
/* Forward declarations */
46
67
namespace Formatters {
47
68
template <typename THead, typename TTail = void >
@@ -89,34 +110,6 @@ class Print
89
110
size_t println (void );
90
111
91
112
virtual void flush () { /* Empty implementation for backward compatibility */ }
92
- /*
93
- template<typename T, typename ...Ts>
94
- _always_inline size_t print(const T &arg, const Ts &...args) {
95
- // This might lead to infinite template instantion
96
- //return print(arg, DefaultFormatter<>(), args...);
97
- size_t n = DefaultFormatter<>().printTo(this, arg);
98
- return n + print(args...);
99
- }
100
-
101
- template<typename T, typename T2, typename ...Ts>
102
- _always_inline auto print(const T &arg, const T2 &arg2, const Ts &...args)
103
- -> enable_if_valid_t<decltype(arg2.printTo(this, arg)), size_t> {
104
- size_t n = arg2.printTo(this, arg);
105
- return n + print(args...);
106
- }
107
-
108
- template<typename T, typename T2, typename T3, typename ...Ts>
109
- _always_inline auto print(const T &arg, const T2 &arg2, const T3 &arg3, const Ts &...args)
110
- -> enable_if_valid_t<decltype(arg2.addOption(arg3)), size_t> {
111
- return print(arg, arg2.addOption(arg3), args...);
112
- }
113
-
114
- template<typename T, typename T2, typename ...Ts>
115
- _always_inline auto print(const T &arg, const T2 &arg2, const Ts &...args)
116
- -> enable_if_valid_t<decltype(DefaultFormatter<>().addOption(arg2)), size_t> {
117
- return print(arg, DefaultFormatter<>().addOption(arg2), args...);
118
- }
119
- */
120
113
121
114
template <typename ...Ts> _always_inline size_t print (const Ts &...args) { return printMultiple (args...); }
122
115
template <typename ...Ts> _always_inline size_t println (const Ts &...args) { size_t t = print (args...); return t + println (); }
@@ -153,31 +146,40 @@ class Print
153
146
// A value and a formatter is specified without any options (or the
154
147
// below overloads have applied the options): Let the formatter
155
148
// print the value.
156
- template <typename T, typename TFormatter, typename ...Ts>
157
- _always_inline auto printMultiple (const T &arg, const TFormatter &formatter, const Ts &...args)
158
- -> enable_if_valid_t<decltype(accepts_formatter(&formatter)), size_t> {
159
- // -> enable_if_valid_t<decltype(formatter.printTo(p, arg)), size_t> {
149
+ template <
150
+ typename T,
151
+ typename TFormatter,
152
+ typename ...Ts,
153
+ detail::enable_if_base_of<Print::Formatter, TFormatter>* = nullptr
154
+ >
155
+ _always_inline size_t printMultiple (const T &arg, const TFormatter &formatter, const Ts &...args) {
160
156
size_t n = formatter.printTo (this , arg);
161
157
return n + printMultiple (args...);
162
158
}
163
159
164
-
165
160
// A value, a formatter and an option is specified: Apply the option
166
161
// to the formatter.
167
- template <typename T, typename TFormatter, typename TOption, typename ...Ts>
168
- _always_inline auto printMultiple (const T &arg, const TFormatter &formatter, const TOption &option, const Ts &...args)
169
- // -> enable_if_valid_t<decltype(formatter + option), size_t> {
170
- -> enable_if_valid_t<decltype(accepts_formatter(&formatter), accepts_option(&option)), size_t> {
162
+ template <
163
+ typename T,
164
+ typename TFormatter,
165
+ typename TOption,
166
+ typename ...Ts,
167
+ detail::enable_if_base_of<Print::Formatter, TFormatter>* = nullptr ,
168
+ detail::enable_if_base_of<Print::FormatterOption, TOption>* = nullptr
169
+ >
170
+ _always_inline size_t printMultiple (const T &arg, const TFormatter &formatter, const TOption &option, const Ts &...args) {
171
171
return printMultiple (arg, formatter + option, args...);
172
172
}
173
173
174
-
175
174
// A value and an option is specified: Look up the default formatter
176
175
// for this value and option and add it to the argument list.
177
- template <typename T, typename TOption, typename ...Ts>
178
- _always_inline auto printMultiple (const T &arg, const TOption &option, const Ts &...args)
179
- // -> enable_if_valid_t<decltype(DefaultFormatterFor(arg, option)), size_t> {
180
- -> enable_if_valid_t<decltype(accepts_option(&option)), size_t> {
176
+ template <
177
+ typename T,
178
+ typename TOption,
179
+ typename ...Ts,
180
+ detail::enable_if_base_of<Print::FormatterOption, TOption>* = nullptr
181
+ >
182
+ _always_inline size_t printMultiple (const T &arg, const TOption &option, const Ts &...args) {
181
183
auto formatter = DefaultFormatterFor (arg, option);
182
184
return printMultiple (arg, formatter, option, args...);
183
185
}
@@ -187,27 +189,35 @@ class Print
187
189
// operator+ to apply each of its arguments to the formatter in
188
190
// turn), but without these the error messages are less clear if
189
191
// incompatible options are mixed (with the below overloads the
190
- // error shows the formatter and the incompatible option, while
191
- // without them only the formatter and the entire option list are
192
+ // error shows the formatter and the incompatible option, while // without them only the formatter and the entire option list are
192
193
// shown.
193
194
//
194
195
// If we keep these, OptionList::addToFormatter and the related
195
196
// operator+ overload can be removed.
196
197
//
197
198
// If we add one more overload for (Value, OptionList, ...), we can
198
199
// also remove the DefaultFormatterForm definition for OptionList.
199
- template <typename T, typename TFormatter, typename THead, typename TTail, typename ...Ts>
200
- _always_inline auto printMultiple (const T &arg, const TFormatter &formatter, const Formatters::OptionList<THead, TTail> &list, const Ts &...args)
201
- // -> enable_if_valid_t<decltype(formatter.addOption(list.head)), size_t> {
202
- -> enable_if_valid_t<decltype(accepts_formatter(&formatter)), size_t> {
200
+ template <
201
+ typename T,
202
+ typename TFormatter,
203
+ typename THead,
204
+ typename TTail,
205
+ typename ...Ts,
206
+ detail::enable_if_base_of<Print::Formatter, TFormatter>* = nullptr
207
+ >
208
+ _always_inline size_t printMultiple (const T &arg, const TFormatter &formatter, const Formatters::OptionList<THead, TTail> &list, const Ts &...args) {
203
209
return printMultiple (arg, formatter, list.head , list.tail , args...);
204
210
}
205
211
206
212
// Base case for the end of the OptionList
207
- template <typename T, typename TFormatter, typename THead, typename ...Ts>
208
- _always_inline auto printMultiple (const T &arg, const TFormatter &formatter, const Formatters::OptionList<THead, void > &list, const Ts &...args)
209
- // -> enable_if_valid_t<decltype(formatter.addOption(list.head)), size_t> {
210
- -> enable_if_valid_t<decltype(accepts_formatter(&formatter)), size_t> {
213
+ template <
214
+ typename T,
215
+ typename TFormatter,
216
+ typename THead,
217
+ typename ...Ts,
218
+ detail::enable_if_base_of<Print::Formatter, TFormatter>* = nullptr
219
+ >
220
+ _always_inline size_t printMultiple (const T &arg, const TFormatter &formatter, const Formatters::OptionList<THead, void > &list, const Ts &...args) {
211
221
return printMultiple (arg, formatter, list.head , args...);
212
222
}
213
223
};
@@ -341,9 +351,6 @@ class DefaultFormatter : public Print::Formatter {
341
351
/* *****************************************************************
342
352
* OptionList */
343
353
344
- // template<typename THead, typename TTail = void>
345
- // class OptionList;
346
-
347
354
template <typename THead, typename TTail>
348
355
class OptionList : public Print ::FormatterOption {
349
356
public:
@@ -353,10 +360,12 @@ class OptionList : public Print::FormatterOption {
353
360
constexpr OptionList (const THead& head, const TTail& tail) : head(head), tail(tail) { }
354
361
355
362
// Apply our contained options to a formatter
356
- template <typename TFormatter>
363
+ template <
364
+ typename TFormatter,
365
+ detail::enable_if_base_of<Print::Formatter, TFormatter>* = nullptr
366
+ >
357
367
auto addToFormatter (const TFormatter& formatter) const
358
- -> enable_if_valid_t<decltype(accepts_formatter(&formatter)),
359
- decltype(formatter + this ->head + this ->tail)>
368
+ -> decltype(formatter + this ->head + this ->tail)
360
369
{
361
370
return formatter + this ->head + this ->tail ;
362
371
}
@@ -366,10 +375,12 @@ class OptionList : public Print::FormatterOption {
366
375
367
376
public:
368
377
// Append another option
369
- template <typename TOption>
378
+ template <
379
+ typename TOption,
380
+ detail::enable_if_base_of<Print::FormatterOption, TOption>* = nullptr
381
+ >
370
382
constexpr auto operator +(const TOption& option) const
371
- -> enable_if_valid_t <decltype(accepts_option(&option)),
372
- OptionList<TOption, decltype(this ->tail + option)>>
383
+ -> OptionList<TOption, decltype(this ->tail + option)>
373
384
{
374
385
return {this ->head , this ->tail + option};
375
386
}
@@ -383,10 +394,12 @@ struct OptionList<THead, void> : public Print::FormatterOption {
383
394
constexpr OptionList (const THead& head) : head(head) { }
384
395
385
396
// Apply our contained options to a formatter
386
- template <typename TFormatter>
397
+ template <
398
+ typename TFormatter,
399
+ detail::enable_if_base_of<Print::Formatter, TFormatter>* = nullptr
400
+ >
387
401
constexpr auto addToFormatter (const TFormatter& formatter) const
388
- -> enable_if_valid_t<decltype(accepts_formatter(&formatter)),
389
- decltype(formatter + this ->head)>
402
+ -> decltype(formatter + this ->head)
390
403
{
391
404
return formatter + this ->head ;
392
405
}
@@ -396,10 +409,12 @@ struct OptionList<THead, void> : public Print::FormatterOption {
396
409
397
410
public:
398
411
// Append another option
399
- template <typename TOption>
412
+ template <
413
+ typename TOption,
414
+ detail::enable_if_base_of<Print::FormatterOption, TOption>* = nullptr
415
+ >
400
416
constexpr auto operator +(const TOption& option) const
401
- -> enable_if_valid_t <decltype(accepts_option(&option)),
402
- OptionList<THead, OptionList<TOption, void >>>
417
+ -> OptionList<THead, OptionList<TOption, void >>
403
418
{
404
419
return {this ->head , option};
405
420
}
@@ -416,10 +431,14 @@ constexpr auto operator +(const TFormatter& formatter, const OptionList<THead, T
416
431
}
417
432
418
433
// Start an OptionList by adding two options
419
- template <typename TOption1, typename TOption2>
434
+ template <
435
+ typename TOption1,
436
+ typename TOption2,
437
+ detail::enable_if_base_of<Print::FormatterOption, TOption1>* = nullptr
438
+ detail::enable_if_base_of<Print::FormatterOption, TOption2>* = nullptr
439
+ >
420
440
constexpr auto operator +(const TOption1& first, const TOption2& second)
421
- -> enable_if_valid_t <decltype(accepts_option(&first), accepts_option(&second)),
422
- OptionList<TOption1, OptionList<TOption2, void >>>
441
+ -> OptionList<TOption1, OptionList<TOption2, void >>
423
442
{
424
443
return {first, second};
425
444
}
@@ -491,14 +510,10 @@ inline size_t Print::print( double n, int prec) { return print(n, FORMAT_
491
510
// create them as wrapper classes, so you can match them directly. Then
492
511
// unpack and repack the values inside whenever you work with them. This
493
512
// avoids the need for these somewhat clunky checks using
494
- // enable_if_valid_t and accepts_formatter. Note that we cannot just
495
- // declare the parameters to be of the superclass type, since we need to
496
- // know the actual formatter/option class too.
497
- //
498
- // Idea: Expose accepts_option and accepts_formatter to debug problems
499
- // when formatters are not accepted (which typically leads to errors
500
- // that only say printTo(..., formatter/option) is not defined. A typical
501
- // error is no (or private) Formatter/FormatterOption base class.
513
+ // enable_if_base_of. Declaring an option would then be a bit more
514
+ // involved and less intuitive, though. Note that we cannot just declare
515
+ // the parameters to be of the superclass type, since we need to know
516
+ // the actual formatter/option class too.
502
517
//
503
518
// Idea: Because DefaultFormatter::printTo accesses options through
504
519
// this, the compiler seems to force the options onto the stack and
0 commit comments