PieterP:
Or look at it from a different perspective: Imagine a CPP (implementation) file containing only the template function above. There's nothing to compile, it doesn't make sense to compile anything, because there's an enormous amount of different types T that could be used. But the compiler doesn't know beforehand which types the template functions are going to be used with, because the user code is in a different translation unit.
Now, I would actually expect to see a linker error in that case, but I'm not sure why that's not what's happening.
OK... I'm starting to feel like an idiot... I'm not "getting it". Let me show you what I tried and maybe it will clear up what my problem is.
In the CPP file, I commented out all of the separate print functions and made one generic function:
template <class T> size_t Print::print (T value, uint8_t base, uint8_t digits)
{
return printInteger (value, base, digits);
}
/////////////////////// unsigned, print() ///////////////////////
//size_t Print::print (uint8_t value, uint8_t base, uint8_t digits)
//{
// return printInteger (value, base, digits);
//}
//
//size_t Print::print (uint16_t value, uint8_t base, uint8_t digits)
//{
// return printInteger (value, base, digits);
//}
//------------ SNIP ------------------
...and in the H file, I commented out the declarations for the separate functions and added one for the new function:
template <class T> size_t print (T, uint8_t=DEC, uint8_t=0); // NEW
// size_t print (uint8_t, uint8_t = DEC, uint8_t = 0);
// size_t print (uint16_t, uint8_t = DEC, uint8_t = 0);
Now, here's the compiler error I get:
```
*/usr/share/arduino-1.0.6/hardware/arduino/cores/arduino/Print.cpp: In instantiation of 'size_t Print::printInteger(T, uint8_t, uint8_t) [with T = const char; size_t = unsigned int; uint8_t = unsigned char]':
/usr/share/arduino-1.0.6/hardware/arduino/cores/arduino/Print.cpp:54:42: required from 'size_t Print::print(T, uint8_t, uint8_t) [with T = const char*; size_t = unsigned int; uint8_t = unsigned char]'
/usr/share/arduino-1.0.6/hardware/arduino/cores/arduino/Print.cpp:309:29: required from here
/usr/share/arduino-1.0.6/hardware/arduino/cores/arduino/Print.cpp:339:6: error: invalid conversion from 'int' to 'const char*' [-fpermissive]
val = -1; // prepare for signed/unsigned test
^
/usr/share/arduino-1.0.6/hardware/arduino/cores/arduino/Print.cpp:343:12: error: wrong type argument to unary minus
value = -value; // ... if actual value is negative invert it (decimal only)
^
/usr/share/arduino-1.0.6/hardware/arduino/cores/arduino/Print.cpp:361:13: error: invalid operands of types 'const char*' and 'uint8_t {aka unsigned char}' to binary 'operator/'
while (val /= base) {
^
/usr/share/arduino-1.0.6/hardware/arduino/cores/arduino/Print.cpp:361:13: error: in evaluation of 'operator/=(const char*, uint8_t {aka unsigned char})'
/usr/share/arduino-1.0.6/hardware/arduino/cores/arduino/Print.cpp:373:17: error: invalid operands of types 'const char*' and 'uint64_t {aka long long unsigned int}' to binary 'operator/'
idx = ((value / intPower (base, digits)) % base); // get a digit**
```
** ^**
The function that the errors are complaining about is the "universal" printInteger(), but as you can see the complaint comes from the compiler trying to send char* values to printInteger... something that I am not doing! My test program, so far, ONLY tries to print a uint16_t value and an int16_t value... no "char *" stuff.
Lastly, here's "printInteger" in case it helps:
template <class T> size_t Print::printInteger (T value, uint8_t base, uint8_t digits)
{
size_t n = 0;
int8_t idx;
int8_t pow;
uint8_t pad;
T val;
val = -1; // prepare for signed/unsigned test
if (val < 0) { // if unsigned it's never less than 0
if ((value < 0) && (base == DEC)) { // less than 0 means it's signed so...
value = -value; // ... if actual value is negative invert it (decimal only)
n += print ((char) '-'); // minus sign
}
}
// if base < 2 or base > 16 then default to decimal (prevent crash if base == 0)
base = base < BIN ? DEC : base > HEX ? DEC : base;
if (base == DEC) { // pad decimal with spaces...
pad = ' '; // ascii space
} else { // ...pad all else with zero
pad = '0'; // ascii zero
}
pow = 1; // initialize power value
val = value; // make a scrap copy of "value"
while (val /= base) {
pow++; // how many digits in "value" with a base of "base"
}
if (digits > 16) {
digits = 16; // no more than 16 character places
}
// print at least req'd number of chars, or more.
digits = pow < digits ? digits : pow;
while (digits--) { // print each character
idx = ((value / intPower (base, digits)) % base); // get a digit
value -= (idx * intPower (base, digits)); // subtract from what's left
if (idx > 9) {
idx += 7; // print hex A-F
}
if (digits < pow) { // if it's part of the actual number
n += print ((char) (idx + '0'));
} else { // else it's a padding character
n += print ((char) pad);
}
}
return n;
}
...and the integer power function that printInteger uses:
uint64_t Print::intPower (uint8_t base, uint8_t exp)
{
uint64_t result = 1;
while (exp--) {
result *= base;
}
return result;
}
I know I SHOULD be able to make this work, I just don't seem to get HOW to do it.