I maintain an Arduino library which uses the following code (simplified) to print results received by infrared.
unsigned long long decodedData; // for 8 and 16 bit cores it is unsigned long decodedData;
Print MySerial;
MySerial.print(decodedData, 16);
Most of the 32 bit arduino cores provide the function size_t Print::print(unsigned long long n, int base)
and compile without errors.
But there are 32 bit cores, which do not provide size_t Print::print(unsigned long long n, int base)
, they only provide size_t Print::print(unsigned long n, int base)
and there I get the expected compile time error
call of overloaded 'print(decodedData, int)' is ambiguous
.
I tried to understand Check if a class has a member function of a given signature but still have no clue.
I want to use
MySerial.print((uint32_t)(decodedData >> 32), 16);
MySerial.print((uint32_t)decodedData & 0xFFFFFFFF, 16);
in case the function size_t Print::print(unsigned long long n, int base)
is not provided.
I tried
template<typename T>
struct has_uint64_print {
template<typename U, size_t (U::*)(unsigned long long, int)> struct SFINAE {
};
template<typename U> static char test(SFINAE<U, &U::print>*);
template<typename U>
static int test(...);
static const bool has64BitPrint = sizeof(test<T>(nullptr)) == sizeof(char);
};
and this works (Thanks to Remy Lebeau) :-).
But this check does not work, since it still references the long long print function (update: and using if constexpr ()
-which is not available for all cores- does not help).
if(has_uint64_print<Print>::has64BitPrint){
MySerial.print(decodedData, 16);
} else {
MySerial.print((uint32_t)(decodedData >> 32), 16);
MySerial.print((uint32_t)decodedData & 0xFFFFFFFF, 16);
}
Is there any chance to avoid this compile error?
BTW. I do not want to substitute all occurences of the 64 bit print with the 2 32 bit prints, only for one seldom used and lazy implemented 32 bit core, since all mainsteam cores work well with the 64 bit print.
CodePudding user response:
With C 11 you can do something like this:
#include <iostream>
#include <iomanip>
#include <type_traits>
// First implementation of printer
class Impl1 {
public:
static void print(uint64_t value, int base) {
std::cout << "64-bit print: " << std::setbase(base) << value << "\n";
}
};
// Second implementation of printer
class Impl2 {
public:
static void print(uint32_t value, int base) {
std::cout << "32-bit print: " << std::setbase(base) << value << "\n";
}
};
// Template to automatically select proper version
template<typename Impl, typename = void>
class Print;
template<typename Impl>
class Print<Impl, typename std::enable_if<std::is_same<decltype(Impl::print), void(uint64_t, int)>::value>::type>
{
public:
static void print(uint64_t value, int base)
{
Impl::print(value, base);
}
};
template<typename Impl>
class Print<Impl, typename std::enable_if<std::is_same<decltype(Impl::print), void(uint32_t, int)>::value>::type>
{
public:
static void print(uint64_t value, int base)
{
Impl::print(static_cast<uint32_t>(value >> 32), base);
Impl::print(static_cast<uint32_t>(value), base);
}
};
int main()
{
Print<Impl1>::print(0x100000001, 16);
Print<Impl2>::print(0x100000001, 16);
}
CodePudding user response:
Problem is poor documentation. I've found this, but it doesn't provide definition of Serial::print
overloads.
I suspect it looks like this:
class Serial
{
public:
...
void print(uint8_t x, int base);
void print(uint16_t x, int base);
void print(uint32_t x, int base);
void print(uint64_t x, int base);
};
So when you use this with unsigned long long
you are expecting it matches overload with uint64_t
, but on some platforms uint64_t
is not a unsigned long long
, but unsigned long
. This mismatch leads to situation that overload resolution can't decide which fallback use and reports error: call of overloaded 'print(decodedData, int)' is ambiguous
.
So instead complicate your life with "Avoid calling of function", just fix your code by explicitly use type uint64_t
. Note this type definition explains your intent, so you should use it definitely.
uint64_t decodedData;
Print MySerial;
MySerial.print(decodedData, 16);
If I'm wrong pleas provide better link to this API documentation. Also include full error log when build fails, so we can see what overloads are available.