I'm trying to overload some functions to work with several data types. This is trivial when I'm passing variables as parameters, thusly:
void fnc(int32_t d){
Serial.println((String) "int32 = " + d);
}
void fnc(char* d){
Serial.println((String) "string = " + d);
}
void fnc(bool d){
if(d){ Serial.println((String) "bool = " + "true"); }
else{ Serial.println((String) "bool = " + "false"); }
}
void fnc(float d){
Serial.println((String) "float = " + d);
}
int32_t anInt = 29339222;
char* aStr = "baloney";
bool aBool = false;
float aFloat = 3.1415;
void setup() {
Serial.begin(9600);
fnc(anInt);
fnc(aStr);
fnc(aBool);
fnc(aFloat);
}
(Note, this is on Arduino, hence setup() instead of main().)
Results in, as you would expect:
int32 = 29339222
string = baloney
bool = false
float = 3.14
Now then, I need to pass literal values as well as variables, like so:
fnc(8728787);
fnc("spamoney");
fnc(true);
fnc(2.718);
The compiler complains about the last line calling fnc(2.718):
sketch_apr1a.ino:34:12: error: call of overloaded 'fnc(double)' is ambiguous
fnc(2.718);
I imagine that this is because as a literal, the parameter provides the compiler with no type information. The actual bits of the value could be interpreted as any 4 byte type (float is 4-bytes on Arduino) and int32_t is a 4 byte type.
So, a couple of questions:
- Why doesn't the compiler have a similar problem with 8728787 as a parameter? That should be just as ambiguous, no?
- How can I make this work? Is there any way to accept all these types as both variables and literals?
Note: I don't want to use a template function because I need to handle the different types with different algorithms.
Note note: I have solved this for now by simply creating multiple functions i.e. fncInt, fncStr, fncBool, etc. This works but...it rubs my OCD the wrong way.
Note note note: This will be part of a library. I'd also like to avoid forcing whoever uses it to do fnc((float) 2.718) for the same reasons as the previous note. Maybe it's silly. I dunno.
Update: help from Miles Budnik and 273K much appreciated. It does indeed compile if I change func(float d) to func(double d).
However, if I try passing an int it fails again.
int anInt2 = 12;
int32_t anInt = 29339222;
float aFloat = 3.1415;
void setup() {
Serial.begin(9600);
fnc(anInt);
fnc(aFloat);
fnc(8728787);
fnc(2.718);
fnc(anInt2);
}
sketch_apr1a.ino:28:13: error: call of overloaded 'fnc(int&)' is ambiguous fnc(anInt2);
Also, if I change from int32 to int64, it also fails, again on the double literal.
void fnc(int64_t d){
//Serial.println((String) "int32 = " + d);
}
int64_t anInt = 29339222;
float aFloat = 3.1415;
void setup() {
Serial.begin(9600);
fnc(anInt);
fnc(aFloat);
fnc(8728787);
fnc(2.718);
fnc(anInt2);
}
sketch_apr1a.ino:27:14: error: call of overloaded 'fnc(long int)' is ambiguous fnc(8728787); ^
I appreciate the advice, I have indeed tried understanding this from the books; I'm afraid I'm just quite confused by the whole thing. Thank you for your help.
Your issue is that the literal
2.718has the typedouble, notfloat, and there is nofnc(double)overload. C++ allows up to one built-in conversion and one user-defined conversion to be implicitly added when performing overload resolution. There are built-in conversions fromdoubletoint32_t,bool, andfloatand none of them are considered "better" than any of the others by the compiler since they are all lossy conversions. Thus the call is ambiguous because it doesn't know if you want to callfnc(int32_t),fnc(bool)orfnc(float).If you made your floating-point overload accept a
doubleinstead of afloatthere would be an exact match, which is considered "better" than a match requiring a conversion, so that would be chosen.Also note that
floattodoubleconversion is also considered "better" than the others mentioned since it's lossless, while the others are lossy, so for a call tofnc(aFloat)the compiler will also choosefnc(double)overfnc(int32_t)orfnc(bool).Alternatively, you can append the
fsuffix to your floating-point literal to explicitly make it afloat. For examplefnc(2.718f)is not ambiguous since it's an exact match forfnc(float).This is a bit complicated. On Arduino,
intcan be either 16-bit or 32-bit, depending on the exact board.8728787will be eitherint(ifintis 32-bit) orlong int(ifintis 16-bit) since8728787is too big to be represented in 16 bits.int32_twill be an alias for eitherint(ifintis 32-bit) orlong int(ifintis 16-bit).In either case, the type of
8728787will exactly matchint32_t. Since it's an exact match,fnc(int32_t)will be chosen over the other options.If you tried to pass a 64-bit integer to
fncyou would get the same ambiguity as thedoubletofloatcase. For example, the following is ambiguous for the same reason as thedoublecall: