36
36
37
37
#define _always_inline __attribute__ ((__always_inline__)) // undefined at end
38
38
39
+ template <typename Check, typename T>
40
+ struct check_type {
41
+ using type = T;
42
+ };
43
+ template <typename Check, typename T> using check_type_t = typename check_type<Check, T>::type;
44
+
39
45
class Print
40
46
{
41
47
private:
@@ -47,6 +53,18 @@ class Print
47
53
public:
48
54
Print () : write_error(0 ) {}
49
55
56
+ struct Formatter { };
57
+
58
+ struct FormatterOptionBase { };
59
+ template <typename TFormatter, typename TValue>
60
+ struct FormatterOption : FormatterOptionBase {
61
+ // Is this the right place to define this? Or perhaps just move
62
+ // down?
63
+ using Formatter = TFormatter;
64
+ TValue value;
65
+ constexpr FormatterOption (const TValue value) : value(value) { }
66
+ };
67
+
50
68
int getWriteError () { return write_error; }
51
69
void clearWriteError () { setWriteError (0 ); }
52
70
@@ -83,12 +101,6 @@ class Print
83
101
_always_inline size_t doPrint (unsigned int n, int f = DEC) { return doPrint ((unsigned long ) n, f); }
84
102
_always_inline size_t doPrint ( float n, int f = 2 ) { return doPrint (( double ) n, f); }
85
103
86
- template <typename Check, typename T>
87
- struct check_type {
88
- using type = T;
89
- };
90
- template <typename Check, typename T> using check_type_t = typename check_type<Check, T>::type;
91
-
92
104
template <typename T, typename F>
93
105
_always_inline auto doPrint (T v, F f )
94
106
-> check_type_t<decltype(f.printTo(this , v)), size_t> {
@@ -98,18 +110,48 @@ class Print
98
110
size_t println (void );
99
111
100
112
virtual void flush () { /* Empty implementation for backward compatibility */ }
113
+ /*
114
+ template<typename T, typename ...Ts>
115
+ _always_inline size_t print(const T &arg, const Ts &...args) {
116
+ // This might lead to infinite template instantion
117
+ //return print(arg, DefaultFormatter<>(), args...);
118
+ size_t n = DefaultFormatter<>().printTo(this, arg);
119
+ return n + print(args...);
120
+ }
121
+
122
+ template<typename T, typename T2, typename ...Ts>
123
+ _always_inline auto print(const T &arg, const T2 &arg2, const Ts &...args)
124
+ -> check_type_t<decltype(arg2.printTo(this, arg)), size_t> {
125
+ size_t n = arg2.printTo(this, arg);
126
+ return n + print(args...);
127
+ }
128
+
129
+ template<typename T, typename T2, typename T3, typename ...Ts>
130
+ _always_inline auto print(const T &arg, const T2 &arg2, const T3 &arg3, const Ts &...args)
131
+ -> check_type_t<decltype(arg2.addOption(arg3)), size_t> {
132
+ return print(arg, arg2.addOption(arg3), args...);
133
+ }
134
+
135
+ template<typename T, typename T2, typename ...Ts>
136
+ _always_inline auto print(const T &arg, const T2 &arg2, const Ts &...args)
137
+ -> check_type_t<decltype(DefaultFormatter<>().addOption(arg2)), size_t> {
138
+ return print(arg, DefaultFormatter<>().addOption(arg2), args...);
139
+ }
140
+ */
101
141
102
142
#if __cplusplus >= 201103L
143
+ template <typename ...Ts> _always_inline size_t print (const Ts &...args);
103
144
template <typename ...Ts> _always_inline size_t println (const Ts &...args) { size_t t = print (args...); return t + println (); }
104
145
#else
105
146
template <typename T> _always_inline size_t println (const T &arg) { size_t t = print (arg); return t + println (); }
106
147
template <typename T, typename T2> _always_inline size_t println (const T &arg1, const T2& arg2) { size_t t = print (arg1, arg2); return t + println (); }
107
148
#endif // __cplusplus >= 201103L
108
149
109
- _always_inline size_t print () { return 0 ; }
150
+ // _always_inline size_t print() { return 0; }
110
151
111
152
/* * Variadic methods **/
112
153
#if __cplusplus >= 201103L // requires C++11
154
+ /*
113
155
template<typename T, typename ...Ts>
114
156
_always_inline size_t print(const T &arg, const Ts &...args) {
115
157
size_t t = doPrint(arg);
@@ -122,6 +164,8 @@ class Print
122
164
size_t t = doPrint(arg, arg2);
123
165
return t + print(args...);
124
166
}
167
+ */
168
+
125
169
/*
126
170
// Some methods take an extra int parameter. If so, use these templates.
127
171
// In a future, it would be nice to make the base/precision a special type.
@@ -142,6 +186,158 @@ class Print
142
186
#endif // __cplusplus >= 201103L
143
187
};
144
188
189
+ class DefaultFormatter ;
190
+
191
+ // TODO: Do we really need a FormatterOption base class? Without it,
192
+ // options can be POD, not needing a constructor. With it, we can
193
+ // prevent accidentally treating things as options which are not, but if
194
+ // we already check a Formatter base class, we can also require that
195
+ // Formatters do not define nonsensical addOption methods (even more,
196
+ // without an explicit FormatterOption, Formatters can even use
197
+ // non-class types as options if they want).
198
+ // TODO: Where to define the "default formatter" for an option? Now, it
199
+ // is our superclass, but it might just as well be defined with a using
200
+ // directive directly here. Or perhaps it should be a method (this
201
+ // might be problematic, since the DefaultFormatter type is incomplete
202
+ // at this point). One completely different approach would be a
203
+ // DefaultFormatterFor template, which gets specialized, but this
204
+ // probably has the problem that once it is instantiated, you can no
205
+ // longer add to it. Using specializations does allow defining a default
206
+ // formatter for a type/option combination (allowing reuse of e.g. HEX
207
+ // for custom types) or for just a type (allowing default formatting of
208
+ // custom types).
209
+ struct FormatOptionBase : Print::FormatterOption<DefaultFormatter, uint8_t > {
210
+ // TODO: We must provide an explicit constructor (if we have a
211
+ // superclass, we are no longer a POD type), and if we do, there is no
212
+ // real point in storing our value in the superclass (better have one
213
+ // extra line here, but more explictness. Inheriting the constructor
214
+ // is even more ugly (needs to repeat superclass template arguments).
215
+ constexpr FormatOptionBase (uint8_t value) : Print::FormatterOption<DefaultFormatter, uint8_t>(value) { }
216
+ };
217
+ struct FormatOptionMinWidth : Print::FormatterOption<DefaultFormatter, uint8_t > {
218
+ constexpr FormatOptionMinWidth (uint8_t value) : Print::FormatterOption<DefaultFormatter, uint8_t>(value) { }
219
+ };
220
+
221
+ #undef HEX
222
+ inline constexpr FormatOptionMinWidth PRINT_MIN_WIDTH (uint8_t min_width) { return {min_width}; }
223
+ inline constexpr FormatOptionBase PRINT_BASE (uint8_t base) { return {base}; }
224
+ constexpr FormatOptionBase HEX = PRINT_BASE(16 );
225
+
226
+ struct DefaultFormatter : Print::Formatter {
227
+ uint8_t base;
228
+ uint8_t min_width;
229
+
230
+ DefaultFormatter (uint8_t base = 10 , uint8_t min_width = 0 )
231
+ : base(base), min_width(min_width)
232
+ { }
233
+
234
+ DefaultFormatter addOption (FormatOptionBase o) const {
235
+ return {o.value , this ->min_width };
236
+ }
237
+
238
+ DefaultFormatter addOption (FormatOptionMinWidth o) const {
239
+ return {this ->base , o.value };
240
+ }
241
+
242
+ size_t printTo (Print *p, int n) const ;
243
+ size_t printTo (Print *p, const char *) const ;
244
+ template <typename T>
245
+ size_t printTo (Print *p, const T&) const ;
246
+ };
247
+
248
+ // TODO: These can probably be moved back inline above (they were split
249
+ // when PrintHelper did not exist, so the Print class was defined
250
+ // between the declaration of DefaultFormatter and these method
251
+ // definitions).
252
+ inline size_t DefaultFormatter::printTo (Print *p, int n) const {
253
+ return p->doPrint (n, this ->base );
254
+ }
255
+
256
+ inline size_t DefaultFormatter::printTo (Print *p, const char * s) const {
257
+ // TODO: Maybe replace bool has_int_options with a more detailed bitwise flag int
258
+ // for better error messages.
259
+ // static_assert(!has_int_options, "Cannot print strings with integer-specific formatting options");
260
+ p->doPrint (s);
261
+ return 0 ;
262
+ }
263
+
264
+ template <typename T>
265
+ inline size_t DefaultFormatter::printTo (Print *p, const T& v) const {
266
+ return p->doPrint (v);
267
+ }
268
+
269
+ // TODO: Should we really need PrintHelper? It was now added allow
270
+ // referencing DefaultFormatter above. These methods could just be
271
+ // out-of-line definitions of methods in Print, but then the complicated
272
+ // method signature must be duplicated. Alternatively, a single method
273
+ // that generates a DefaultFormatter object could possibly be out of
274
+ // line (though, thinking on it, both of these options might not work,
275
+ // since they need DefaultFormatter as a return type in a
276
+ // declaration...).
277
+ class PrintHelper {
278
+ public:
279
+ template <typename T, typename ...Ts>
280
+ static _always_inline size_t printTo (Print * p, const T &arg, const Ts &...args) {
281
+ // This might lead to infinite template instantion
282
+ // return print(arg, DefaultFormatter<>(), args...);
283
+ size_t n = DefaultFormatter ().printTo (p, arg);
284
+ return n + printTo (p, args...);
285
+ }
286
+ /*
287
+ template<typename T, typename T2, typename ...Ts>
288
+ static _always_inline auto printTo(Print * p, const T &arg, const T2 &arg2, const Ts &...args)
289
+ -> check_type_t<decltype(arg2.printTo(p, arg)), size_t> {
290
+ size_t n = arg2.printTo(p, arg);
291
+ return n + printTo(p, args...);
292
+ }
293
+ */
294
+ static void accepts_formatter (const Print::Formatter*);
295
+
296
+ template <typename T, typename T2, typename ...Ts>
297
+ static _always_inline auto printTo (Print * p, const T &arg, const T2 &arg2, const Ts &...args)
298
+ -> check_type_t<decltype(accepts_formatter(&arg2)), size_t> {
299
+ // -> check_type_t<decltype(arg2.printTo(p, arg)), size_t> {
300
+ size_t n = arg2.printTo (p, arg);
301
+ return n + printTo (p, args...);
302
+ }
303
+
304
+
305
+ template <typename T, typename T2, typename T3, typename ...Ts>
306
+ static _always_inline auto printTo (Print * p, const T &arg, const T2 &arg2, const T3 &arg3, const Ts &...args)
307
+ -> check_type_t<decltype(arg2.addOption(arg3)), size_t> {
308
+ return printTo (p, arg, arg2.addOption (arg3), args...);
309
+ }
310
+
311
+ static void accepts_option (const Print::FormatterOptionBase*);
312
+
313
+ template <typename T, typename T2, typename ...Ts>
314
+ static _always_inline auto printTo (Print * p, const T &arg, const T2 &arg2, const Ts &...args)
315
+ // -> check_type_t<decltype(typename T2::Formatter().addOption(arg2)), size_t> {
316
+ -> check_type_t<decltype(accepts_option(&arg2)), size_t> {
317
+ // accepts_option(&arg2);
318
+ return printTo (p, arg, typename T2::Formatter ().addOption (arg2), args...);
319
+ }
320
+
321
+ static _always_inline size_t printTo (Print *) { return 0 ; }
322
+ };
323
+
324
+ template <typename ...Ts>
325
+ _always_inline size_t Print::print (const Ts &...args) { return PrintHelper::printTo (this , args...); }
145
326
#undef _always_inline
146
327
147
328
#endif
329
+
330
+ // Idea: Raise errors when DefaultFormatter is used with int-only
331
+ // options (e.g. integer base), but prints a string or otherwise
332
+ // incompatible type. This can be done by extending DefaultFormatter
333
+ // with a bitmask or bool template argument(s) that track what options
334
+ // have been set, so they can be static_asserted against in specific
335
+ // printTo versions. This does require that *all* methods in
336
+ // DefaultFormatter are always_inline, to prevent duplicates. This means
337
+ // the actual printing code must again live elsewhere, which might not
338
+ // be ideal. Also, error messages are then complicated with these
339
+ // template arguments. This can be solved by doing just the checks and
340
+ // forwarding *all* printTo calls to another class (including a
341
+ // catch-all template), so that when you try printing any unsupported
342
+ // types you still get the proper "no such method to call, candiates
343
+ // are..." error message.
0 commit comments