|
@@ -74,12 +74,12 @@ CALL_IF_EXISTS_IMPL(SerialFeature, features, SerialFeature::None);
|
74
|
74
|
// for any type other than double/float. For double/float, a conversion exists so the call will be invisible.
|
75
|
75
|
struct EnsureDouble {
|
76
|
76
|
double a;
|
77
|
|
- FORCE_INLINE operator double() { return a; }
|
|
77
|
+ operator double() { return a; }
|
78
|
78
|
// If the compiler breaks on ambiguity here, it's likely because print(X, base) is called with X not a double/float, and
|
79
|
79
|
// a base that's not a PrintBase value. This code is made to detect the error. You MUST set a base explicitly like this:
|
80
|
80
|
// SERIAL_PRINT(v, PrintBase::Hex)
|
81
|
|
- FORCE_INLINE EnsureDouble(double a) : a(a) {}
|
82
|
|
- FORCE_INLINE EnsureDouble(float a) : a(a) {}
|
|
81
|
+ EnsureDouble(double a) : a(a) {}
|
|
82
|
+ EnsureDouble(float a) : a(a) {}
|
83
|
83
|
};
|
84
|
84
|
|
85
|
85
|
// Using Curiously-Recurring Template Pattern here to avoid virtual table cost when compiling.
|
|
@@ -136,70 +136,90 @@ struct SerialBase {
|
136
|
136
|
void flushTX() { CALL_IF_EXISTS(void, SerialChild, flushTX); }
|
137
|
137
|
|
138
|
138
|
// Glue code here
|
139
|
|
- FORCE_INLINE void write(const char *str) { while (*str) write(*str++); }
|
140
|
|
- FORCE_INLINE void write(const uint8_t *buffer, size_t size) { while (size--) write(*buffer++); }
|
141
|
|
- FORCE_INLINE void print(const char *str) { write(str); }
|
|
139
|
+ void write(const char *str) { while (*str) write(*str++); }
|
|
140
|
+ void write(const uint8_t *buffer, size_t size) { while (size--) write(*buffer++); }
|
|
141
|
+ void print(char *str) { write(str); }
|
|
142
|
+ void print(const char *str) { write(str); }
|
142
|
143
|
// No default argument to avoid ambiguity
|
143
|
|
- NO_INLINE void print(char c, PrintBase base) { printNumber((signed long)c, (uint8_t)base); }
|
144
|
|
- NO_INLINE void print(unsigned char c, PrintBase base) { printNumber((unsigned long)c, (uint8_t)base); }
|
145
|
|
- NO_INLINE void print(int c, PrintBase base) { printNumber((signed long)c, (uint8_t)base); }
|
146
|
|
- NO_INLINE void print(unsigned int c, PrintBase base) { printNumber((unsigned long)c, (uint8_t)base); }
|
147
|
|
- void print(unsigned long c, PrintBase base) { printNumber((unsigned long)c, (uint8_t)base); }
|
148
|
|
- void print(long c, PrintBase base) { printNumber((signed long)c, (uint8_t)base); }
|
149
|
|
- void print(EnsureDouble c, int digits) { printFloat(c, digits); }
|
|
144
|
+
|
|
145
|
+ // Define print for every fundamental integer type, to ensure that all redirect properly
|
|
146
|
+ // to the correct underlying implementation.
|
|
147
|
+
|
|
148
|
+ // Prints are performed with a single size, to avoid needing multiple print functions.
|
|
149
|
+ // The fixed integer size used for prints will be the larger of long or a pointer.
|
|
150
|
+ #if __LONG_WIDTH__ >= __INTPTR_WIDTH__
|
|
151
|
+ typedef long int_fixed_print_t;
|
|
152
|
+ typedef unsigned long uint_fixed_print_t;
|
|
153
|
+ #else
|
|
154
|
+ typedef intptr_t int_fixed_print_t;
|
|
155
|
+ typedef uintptr_t uint_fixed_print_t;
|
|
156
|
+
|
|
157
|
+ FORCE_INLINE void print(intptr_t c, PrintBase base) { printNumber_signed(c, base); }
|
|
158
|
+ FORCE_INLINE void print(uintptr_t c, PrintBase base) { printNumber_unsigned(c, base); }
|
|
159
|
+ #endif
|
|
160
|
+
|
|
161
|
+ FORCE_INLINE void print(char c, PrintBase base) { printNumber_signed(c, base); }
|
|
162
|
+ FORCE_INLINE void print(short c, PrintBase base) { printNumber_signed(c, base); }
|
|
163
|
+ FORCE_INLINE void print(int c, PrintBase base) { printNumber_signed(c, base); }
|
|
164
|
+ FORCE_INLINE void print(long c, PrintBase base) { printNumber_signed(c, base); }
|
|
165
|
+ FORCE_INLINE void print(unsigned char c, PrintBase base) { printNumber_unsigned(c, base); }
|
|
166
|
+ FORCE_INLINE void print(unsigned short c, PrintBase base) { printNumber_unsigned(c, base); }
|
|
167
|
+ FORCE_INLINE void print(unsigned int c, PrintBase base) { printNumber_unsigned(c, base); }
|
|
168
|
+ FORCE_INLINE void print(unsigned long c, PrintBase base) { printNumber_unsigned(c, base); }
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+ void print(EnsureDouble c, int digits) { printFloat(c, digits); }
|
150
|
172
|
|
151
|
173
|
// Forward the call to the former's method
|
152
|
|
- FORCE_INLINE void print(char c) { print(c, PrintBase::Dec); }
|
153
|
|
- FORCE_INLINE void print(unsigned char c) { print(c, PrintBase::Dec); }
|
154
|
|
- FORCE_INLINE void print(int c) { print(c, PrintBase::Dec); }
|
155
|
|
- FORCE_INLINE void print(unsigned int c) { print(c, PrintBase::Dec); }
|
156
|
|
- FORCE_INLINE void print(unsigned long c) { print(c, PrintBase::Dec); }
|
157
|
|
- FORCE_INLINE void print(long c) { print(c, PrintBase::Dec); }
|
158
|
|
- FORCE_INLINE void print(double c) { print(c, 2); }
|
159
|
|
-
|
160
|
|
- FORCE_INLINE void println(const char s[]) { print(s); println(); }
|
161
|
|
- FORCE_INLINE void println(char c, PrintBase base) { print(c, base); println(); }
|
162
|
|
- FORCE_INLINE void println(unsigned char c, PrintBase base) { print(c, base); println(); }
|
163
|
|
- FORCE_INLINE void println(int c, PrintBase base) { print(c, base); println(); }
|
164
|
|
- FORCE_INLINE void println(unsigned int c, PrintBase base) { print(c, base); println(); }
|
165
|
|
- FORCE_INLINE void println(long c, PrintBase base) { print(c, base); println(); }
|
166
|
|
- FORCE_INLINE void println(unsigned long c, PrintBase base) { print(c, base); println(); }
|
167
|
|
- FORCE_INLINE void println(double c, int digits) { print(c, digits); println(); }
|
168
|
|
- FORCE_INLINE void println() { write('\r'); write('\n'); }
|
|
174
|
+
|
|
175
|
+ // Default implementation for anything without a specialization
|
|
176
|
+ // This handles integers since they are the most common
|
|
177
|
+ template <typename T>
|
|
178
|
+ void print(T c) { print(c, PrintBase::Dec); }
|
|
179
|
+
|
|
180
|
+ void print(float c) { print(c, 2); }
|
|
181
|
+ void print(double c) { print(c, 2); }
|
|
182
|
+
|
|
183
|
+ void println(char *s) { print(s); println(); }
|
|
184
|
+ void println(const char *s) { print(s); println(); }
|
|
185
|
+ void println(float c, int digits) { print(c, digits); println(); }
|
|
186
|
+ void println(double c, int digits) { print(c, digits); println(); }
|
|
187
|
+ void println() { write('\r'); write('\n'); }
|
|
188
|
+
|
|
189
|
+ // Default implementations for types without a specialization. Handles integers.
|
|
190
|
+ template <typename T>
|
|
191
|
+ void println(T c, PrintBase base) { print(c, base); println(); }
|
|
192
|
+
|
|
193
|
+ template <typename T>
|
|
194
|
+ void println(T c) { println(c, PrintBase::Dec); }
|
169
|
195
|
|
170
|
196
|
// Forward the call to the former's method
|
171
|
|
- FORCE_INLINE void println(char c) { println(c, PrintBase::Dec); }
|
172
|
|
- FORCE_INLINE void println(unsigned char c) { println(c, PrintBase::Dec); }
|
173
|
|
- FORCE_INLINE void println(int c) { println(c, PrintBase::Dec); }
|
174
|
|
- FORCE_INLINE void println(unsigned int c) { println(c, PrintBase::Dec); }
|
175
|
|
- FORCE_INLINE void println(unsigned long c) { println(c, PrintBase::Dec); }
|
176
|
|
- FORCE_INLINE void println(long c) { println(c, PrintBase::Dec); }
|
177
|
|
- FORCE_INLINE void println(double c) { println(c, 2); }
|
|
197
|
+ void println(float c) { println(c, 2); }
|
|
198
|
+ void println(double c) { println(c, 2); }
|
178
|
199
|
|
179
|
200
|
// Print a number with the given base
|
180
|
|
- NO_INLINE void printNumber(unsigned long n, const uint8_t base) {
|
181
|
|
- if (!base) return; // Hopefully, this should raise visible bug immediately
|
182
|
|
-
|
|
201
|
+ NO_INLINE void printNumber_unsigned(uint_fixed_print_t n, PrintBase base) {
|
183
|
202
|
if (n) {
|
184
|
203
|
unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
|
185
|
204
|
int8_t i = 0;
|
186
|
205
|
while (n) {
|
187
|
|
- buf[i++] = n % base;
|
188
|
|
- n /= base;
|
|
206
|
+ buf[i++] = n % (uint_fixed_print_t)base;
|
|
207
|
+ n /= (uint_fixed_print_t)base;
|
189
|
208
|
}
|
190
|
209
|
while (i--) write((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10)));
|
191
|
210
|
}
|
192
|
211
|
else write('0');
|
193
|
212
|
}
|
194
|
|
- void printNumber(signed long n, const uint8_t base) {
|
195
|
|
- if (base == 10 && n < 0) {
|
|
213
|
+
|
|
214
|
+ NO_INLINE void printNumber_signed(int_fixed_print_t n, PrintBase base) {
|
|
215
|
+ if (base == PrintBase::Dec && n < 0) {
|
196
|
216
|
n = -n; // This works because all platforms Marlin's builds on are using 2-complement encoding for negative number
|
197
|
217
|
// On such CPU, changing the sign of a number is done by inverting the bits and adding one, so if n = 0x80000000 = -2147483648 then
|
198
|
218
|
// -n = 0x7FFFFFFF + 1 => 0x80000000 = 2147483648 (if interpreted as unsigned) or -2147483648 if interpreted as signed.
|
199
|
219
|
// On non 2-complement CPU, there would be no possible representation for 2147483648.
|
200
|
220
|
write('-');
|
201
|
221
|
}
|
202
|
|
- printNumber((unsigned long)n , base);
|
|
222
|
+ printNumber_unsigned((uint_fixed_print_t)n , base);
|
203
|
223
|
}
|
204
|
224
|
|
205
|
225
|
// Print a decimal number
|
|
@@ -218,7 +238,7 @@ struct SerialBase {
|
218
|
238
|
// Extract the integer part of the number and print it
|
219
|
239
|
unsigned long int_part = (unsigned long)number;
|
220
|
240
|
double remainder = number - (double)int_part;
|
221
|
|
- printNumber(int_part, 10);
|
|
241
|
+ printNumber_unsigned(int_part, PrintBase::Dec);
|
222
|
242
|
|
223
|
243
|
// Print the decimal point, but only if there are digits beyond
|
224
|
244
|
if (digits) {
|
|
@@ -227,7 +247,7 @@ struct SerialBase {
|
227
|
247
|
while (digits--) {
|
228
|
248
|
remainder *= 10.0;
|
229
|
249
|
unsigned long toPrint = (unsigned long)remainder;
|
230
|
|
- printNumber(toPrint, 10);
|
|
250
|
+ printNumber_unsigned(toPrint, PrintBase::Dec);
|
231
|
251
|
remainder -= toPrint;
|
232
|
252
|
}
|
233
|
253
|
}
|