arduino / arduino-builder

A command line tool for compiling Arduino sketches
GNU General Public License v2.0
458 stars 114 forks source link

Template is treated as operator< #360

Closed kokosxD closed 4 years ago

kokosxD commented 4 years ago

When an function from template class SecondClass calls a template function which takes a function as it template argument from template class FirstClass, the Arduino IDE treats the <> as the operator<. Note that this is happening ONLY with template classes. If I remove the templates from the classes without removing the templates from their member functions, it compiles fine.

Apart from the Arduino-specific code, it compiles without any errors or warnings in Visual Studio 2019.

How to reproduce


// Returns the length of the given string
template<typename _Ty1>
const unsigned int len(const _Ty1* const _str) noexcept{
    for(unsigned int length = 0; ; length++){
        if(! _str[length]){
            return length;
        }
    }
}

// Comparison function
template<typename _Ty1>
inline const bool CaseSensitive(const _Ty1& _ch1, const _Ty1& _ch2) noexcept{
    return _ch1 == _ch2;
}

// Template type of comparison function
template<typename _Ty1>
using CompFunc = const bool(*)(const _Ty1&, const _Ty1&) noexcept;

template<typename _Ty1>
class FirstClass final{
public:

    // Takes a function as its template argument and 2 characters to compare
    template<CompFunc<_Ty1> _comp>
    inline const bool FirstAreSame(const _Ty1& _ch1, const _Ty1& _ch2) const noexcept{
        return _comp(_ch1, _ch2);
    }
};

template<typename _Ty1>
class SecondClass final{
public:
    const bool SecondAreSame(const FirstClass<_Ty1>& _first_object, const _Ty1* const _str1, const _Ty1* const _str2) const noexcept{
        const unsigned int str1_len = len<_Ty1>(_str1);
        const unsigned int str2_len = len<_Ty1>(_str2);

        if(str1_len != str2_len){
            return false;
        }

        for(unsigned int character = 0; character < str1_len; character++){

            // It calls FirstClass<_Ty1>::FirstAreSame<>
            if(! (_first_object.FirstAreSame<CaseSensitive>(_str1[character], _str2[character]))){
                return false;
            }
        }

        return true;
    }
};

void setup(){}

void loop(){
    const FirstClass<char> first_object = FirstClass<char>();
    const SecondClass<char> second_object = SecondClass<char>();
    const char* const str1 = "this is a test";
    const char* const str2 = "this is a test";
    if(second_object.SecondAreSame(first_object, str1, str2)){
        Serial.write("Strings are same!");
    }
    else{
        Serial.write("Strings are NOT same!");
    }

    exit(0);
}

Error message

C:\Users\username\Arduino Project\Arduino.ino: In instantiation of 'const bool SecondClass<_Ty1>::SecondAreSame(const FirstClass<_Ty1>&, const _Ty1*, const _Ty1*) const [with _Ty1 = char]':
C:\Users\username\Arduino Project\Arduino.ino:63:57:   required from here
Arduino:47:36: error: invalid operands of types '<unresolved overloaded function type>' and '<unresolved overloaded function type>' to binary 'operator<'
    if(! (_first_object.FirstAreSame<CaseSensitive>(_str1[character], _str2[character]))){
          ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~
exit status 1
invalid operands of types '<unresolved overloaded function type>' and '<unresolved overloaded function type>' to binary 'operator<'

What I'm using

matthijskooijman commented 4 years ago

This is not an Arduino-specific problem, I think you're code is incorrect. Running this against normal g++ produces the same error. Using clang provides a hint about the problem:

matthijs@grubby:~$ clang-10 -c testfoo.cpp 
testfoo.cpp:19:58: error: exception specifications are not allowed in type aliases
using CompFunc = const bool(*)(const _Ty1&, const _Ty1&) noexcept;
                                                         ^
testfoo.cpp:46:24: error: missing 'template' keyword prior to dependent template name 'FirstAreSame'
   if(! (_first_object.FirstAreSame<CaseSensitive>(_str1[character], _str2[character]))){
                       ^
testfoo.cpp:62:19: note: in instantiation of member function 'SecondClass<char>::SecondAreSame' requested here
 if(second_object.SecondAreSame(first_object, str1, str2)){
                  ^

(Here, testfoo.cpp. is your code minus the references to Serial and exit)

So apparently you need to explicitly state to the compiler that FirstAreSame is a template using the template keyword. I'm not sure off-hand what the exact rules are here, but I think you can figure it out from here.

Anyway, not an Arduino bug, so I'm closing this.

kokosxD commented 4 years ago

So apparently you need to explicitly state to the compiler that FirstAreSame is a template using the template keyword.

I added the template keyword before the function name and it worked!

// It calls FirstClass<_Ty1>::FirstAreSame<>
if(! (_first_object.template FirstAreSame<CaseSensitive>(_str1[character], _str2[character]))){
    return false;
}

Also, I forgot to initialize Serial, so first I didn't get any output. Thank you so much!