Reputation: 23
I'm working on an Arduino library where some methods will have to behave differently if a float is passed instead of an integer value. It has to be this way since the underlying function has to do more mathematical operations if a float is passed rather than an integer.
Here's some isolated, stripped down code to demonstrate the problem.
void printdata(uint32_t data)
{
Serial.printf("Printing unsigned integer %d", data);
}
void printdata(float data)
{
Serial.printf("Printing float %i", data);
}
template<typename Data>
void myTemplate(const Data& d)
{
printdata(d);
}
I'd like the template function to call printdata(uint32_t) if pass an integer number (such as printdata(1000)
). I'd also like the template function to call printdata(float) when a float is passed (such as printdata(10.00)
).
However, when I call printdata(1000)
my compiler gives me an error saying call of overloaded 'printdata(const double&)' is ambiguous
.
Can this be achieved without having to manually cast everything? I want the library I'm writing to be as user-friendly and less verbose as possible. Manually casting everything isn't really a great solution for me.
Thanks!
Upvotes: 1
Views: 134
Reputation: 2819
Consider std::is_integral
from the <type_traits>
header and also C++17 constexpr if
.
std::is_integral
will tell you if a given type is an integral type at compile time.
constexpr if
is basically an if statement that is evaluated at compile time. The branch not taken is never executed (in fact it's not even compiled).
#include <type_traits>
void printdata(uint32_t data)
{
Serial.printf("Printing unsigned integer %d", data);
}
void printdata(float data)
{
Serial.printf("Printing float %i", data);
}
template<typename Data>
void myTemplate(const Data& d)
{
// uses C++17 constexpr if
if constexpr (std::is_integral_v<Data>) printdata(static_cast<uint32_t>(d));
else printdata(static_cast<float>(d));
}
EDIT: However, if you can't use C++17 you will need to use fancier template logic, which I'll demonstrate next. I'll also avoid using anything from the type_traits
header because you mentioned you don't have access to it for some reason?
NOTE: The code I'm about to show is very gross and I would never recommend writing it outside of academic exercises or curiosity. The reason for this is the lack of the type_traits
header, which means we have to define much more than we would otherwise have to with e.g. SFINAE. Additionally, most template code uses lots of type_traits
stuff, which makes this even more awkward.
So at this point (without the type_traits
header or C++17), I would suggest not using templates and simply make overloads for every type you want to use printdata
with, which avoids the issue of ambiguous overloads.
// given a type T, defines a static member function called f that routes to the correct form of printdata.
// default implementation goes to int version.
template<typename T> struct _get_version { static void f(T val) { printdata(static_cast<uint32_t>(val)); } };
// specialize this for all the floating point types (float, double, and long double).
template<> struct _get_version<float> { static void f(float val) { printdata(static_cast<float>(val)); } };
template<> struct _get_version<double> { static void f(double val) { printdata(static_cast<float>(val)); } };
template<> struct _get_version<long double> { static void f(long double val) { printdata(static_cast<float>(val)); } };
template<typename Data>
void myTemplate(Data d)
{
// get the version Data should use, then use its internal f function
_get_version<Data>::f(d);
}
Upvotes: 1
Reputation: 10750
Sometimes we have to provide additional overloads to disambiguate between options. In this case, we'd provide additional overloads for printdata
.
The best option is to just add additional printdata
functions for int
s, double
s, and other datatypes you care about, but if you're fine printing them with as unsigned int
s and float
s, you can just cast them.
void printdata(uint32_t data)
{
Serial.printf("Printing unsigned integer %d", data);
}
// ints should be printed as unsigned ints?
void printdata(int data) {
printdata((uint32_t)data); //Call unsigned version
}
void printdata(float data)
{
Serial.printf("Printing float %i", data);
}
//doubles should be printed as floats
void printdata(double data)
{
printdata((float)data);
}
Upvotes: 0