Home > OS >  Avoid calling of function size_t Print::print(unsigned long long n, int base) if it is not implement
Avoid calling of function size_t Print::print(unsigned long long n, int base) if it is not implement

Time:12-02

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.

  • Related