This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Compiler: c2000 18.1.4.LTS produces false error #1581-D

Tool/software: TI C/C++ Compiler

I'm validating 18.1.4.LTS on our code base and I'm getting a false diagnostic:

>> Compilation failure
ServoLibrary/motionControl/brake/subdir_rules.mk:9: recipe for target 'ServoLibrary/motionControl/brake/Brake.obj' failed
"C:\devel\Servo\ServoApplication\ServoLibrary\hal\TMS320C28346\include\..\..\..\service\math\Absolute.h", line 74: error #1581-D: "service::math::Absolute<T> operator+(const service::math::Relative<T> &, const service::math::Absolute<T> &)" declares a non-template function -- add <> to refer to a template instance
"C:\devel\Servo\ServoApplication\ServoLibrary\hal\TMS320C28346\include\..\..\..\service\math\Absolute.h", line 90: error #1581-D: "service::math::Absolute<T> operator-(const service::math::Relative<T> &, const service::math::Absolute<T> &)" declares a non-template function -- add <> to refer to a template instance
"C:\devel\Servo\ServoApplication\ServoLibrary\hal\TMS320C28346\include\..\..\..\service\math\Relative.h", line 130: error #1581-D: "service::math::Absolute<T> operator+(const service::math::Absolute<T> &, const service::math::Relative<T> &)" declares a non-template function -- add <> to refer to a template instance
"C:\devel\Servo\ServoApplication\ServoLibrary\hal\TMS320C28346\include\..\..\..\service\math\Relative.h", line 160: error #1581-D: "service::math::Absolute<T> operator-(const service::math::Absolute<T> &, const service::math::Relative<T> &)" declares a non-template function -- add <> to refer to a template instance
4 errors detected in the compilation of ...

Apparently, the compiler thinks that non-inline friends of a template class must be templates themselves. However in this case the friends are defined inline in a partner class and the friend declaration exists to enable argument dependent lookup in cases where one of the two arguments must be constructed implicitly from a compatible type.

Here is the relevant code minus comments (to make it more compact):

#ifndef _SERVICE_MATH_ABSOLUTE_H_
#define _SERVICE_MATH_ABSOLUTE_H_

namespace service {
namespace math {

template <typename>
struct Relative;

template <typename T>
struct Absolute
{
    Absolute(T const& rValue) : mValue(rValue) {}

    friend Absolute<T> operator +(Absolute<T> const& rLSummand,
                                  Relative<T> const& rRSummand)
    {
        return rLSummand.mValue + rRSummand.mValue;
    }

    // error #1581
    friend Absolute<T> operator +(Relative<T> const&, Absolute<T> const&);

    friend Absolute<T> operator -(Absolute<T> const& rMinuend,
                                  Relative<T> const& rSubtrahend)
    {
        return rMinuend.mValue - rSubtrahend.mValue;
    }

    // error #1581
    friend Absolute<T> operator -(Relative<T> const&, Absolute<T> const&);

    friend Relative<T> operator -(Absolute<T> const& rMinuend,
                                  Absolute<T> const& rSubtrahend)
    {
        return rMinuend.mValue - rSubtrahend.mValue;
    }

    friend bool operator ==(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
    {
        return rLhs.mValue == rRhs.mValue;
    }

    friend bool operator !=(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
    {
        return rLhs.mValue != rRhs.mValue;
    }

    friend bool operator <(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
    {
        return rLhs.mValue < rRhs.mValue;
    }

    friend bool operator <=(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
    {
        return rLhs.mValue <= rRhs.mValue;
    }

    friend bool operator >(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
    {
        return rLhs.mValue > rRhs.mValue;
    }

    friend bool operator >=(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
    {
        return rLhs.mValue >= rRhs.mValue;
    }

    T mValue;
};

} /* namespace math */
} /* namespace service */

#endif /* _SERVICE_MATH_ABSOLUTE_H_ */


#ifndef _SERVICE_MATH_RELATIVE_H_
#define _SERVICE_MATH_RELATIVE_H_

namespace service {
namespace math {

template <typename> struct Absolute;
template <typename> struct Relative;

template <typename T>
inline Relative<T> operator %(Relative<T> const& rDivident,
                              Relative<T> const& rQuotient)
{
    return rDivident.mValue % rQuotient.mValue;
}

template <typename T>
struct Relative
{
    Relative(T const& rValue) : mValue(rValue) {}

    friend Relative<T> operator +(Relative<T> const& rOp)
    {
        return rOp;
    }

    friend Relative<T> operator -(Relative<T> const& rOp)
    {
        return - rOp.mValue;
    }

    // error #1581
friend Absolute<T> operator +(Absolute<T> const&, Relative<T> const&); friend Absolute<T> operator +(Relative<T> const& rLSummand, Absolute<T> const& rRSummand) { return rLSummand.mValue + rRSummand.mValue; } friend Relative<T> operator +(Relative<T> const& rLSummand, Relative<T> const& rRSummand) { return rLSummand.mValue + rRSummand.mValue; } // error #1581
friend Absolute<T> operator -(Absolute<T> const&, Relative<T> const&); friend Absolute<T> operator -(Relative<T> const& rMinuend, Absolute<T> const& rSubtrahend) { return rMinuend.mValue - rSubtrahend.mValue; } friend Relative<T> operator -(Relative<T> const& rMinuend, Relative<T> const& rSubtrahend) { return rMinuend.mValue - rSubtrahend.mValue; } friend Relative<T> operator *(Relative<T> const& rLFactor, Relative<T> const& rRFactor) { return rLFactor.mValue * rRFactor.mValue; } friend Relative<T> operator /(Relative<T> const& rDivident, Relative<T> const& rQuotient) { return rDivident.mValue / rQuotient.mValue; } friend Relative<T> operator %(Relative<T> const& rDivident, Relative<T> const& rDivisor) { return operator %<T>(rDivident, rDivisor); } friend bool operator ==(Relative<T> const& rLhs, Relative<T> const& rRhs) { return rLhs.mValue == rRhs.mValue; } friend bool operator !=(Relative<T> const& rLhs, Relative<T> const& rRhs) { return rLhs.mValue != rRhs.mValue; } friend bool operator <(Relative<T> const& rLhs, Relative<T> const& rRhs) { return rLhs.mValue < rRhs.mValue; } friend bool operator <=(Relative<T> const& rLhs, Relative<T> const& rRhs) { return rLhs.mValue <= rRhs.mValue; } friend bool operator >(Relative<T> const& rLhs, Relative<T> const& rRhs) { return rLhs.mValue > rRhs.mValue; } friend bool operator >=(Relative<T> const& rLhs, Relative<T> const& rRhs) { return rLhs.mValue >= rRhs.mValue; } T mValue; }; #pragma diag_pop } /* namespace math */ } /* namespace service */ #endif /* _SERVICE_MATH_RELATIVE_H_ */

  • I'm not completely certain, but I think you have the problem discussed in this FAQ (not from TI).  The file attached below has some changes to consider.  Compare it to the first of the header files you show in your post.  These changes are adapted from the first solution discussed in the FAQ.  

    Thanks and regards,

    -George

    absolute.h.txt
    #ifndef _SERVICE_MATH_ABSOLUTE_H_
    #define _SERVICE_MATH_ABSOLUTE_H_
    
    namespace service {
    namespace math {
    
    template <typename>
    struct Relative;
    
    // Start addition
    template <typename>
    struct Absolute;
    
    template<typename T>
    Absolute<T> operator +(Relative<T> const&, Absolute<T> const&);
    
    template<typename T>
    Absolute<T> operator -(Relative<T> const&, Absolute<T> const&);
    // End addition
    
    template <typename T>
    struct Absolute
    {
        Absolute(T const& rValue) : mValue(rValue) {}
    
        friend Absolute<T> operator +(Absolute<T> const& rLSummand,
                                      Relative<T> const& rRSummand)
        {
            return rLSummand.mValue + rRSummand.mValue;
        }
    
        // error #1581 - fixed
        friend Absolute<T> operator + <>(Relative<T> const&, Absolute<T> const&);
    
        friend Absolute<T> operator -(Absolute<T> const& rMinuend,
                                      Relative<T> const& rSubtrahend)
        {
            return rMinuend.mValue - rSubtrahend.mValue;
        }
    
        // error #1581 - fixed
        friend Absolute<T> operator - <>(Relative<T> const&, Absolute<T> const&);
    
        friend Relative<T> operator -(Absolute<T> const& rMinuend,
                                      Absolute<T> const& rSubtrahend)
        {
            return rMinuend.mValue - rSubtrahend.mValue;
        }
    
        friend bool operator ==(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
        {
            return rLhs.mValue == rRhs.mValue;
        }
    
        friend bool operator !=(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
        {
            return rLhs.mValue != rRhs.mValue;
        }
    
        friend bool operator <(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
        {
            return rLhs.mValue < rRhs.mValue;
        }
    
        friend bool operator <=(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
        {
            return rLhs.mValue <= rRhs.mValue;
        }
    
        friend bool operator >(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
        {
            return rLhs.mValue > rRhs.mValue;
        }
    
        friend bool operator >=(Absolute<T> const& rLhs, Absolute<T> const& rRhs)
        {
            return rLhs.mValue >= rRhs.mValue;
        }
    
        T mValue;
    };
    
    } /* namespace math */
    } /* namespace service */
    
    #endif /* _SERVICE_MATH_ABSOLUTE_H_ */
    

  • The problem is, they are not template functions, and they are not supposed to be.

    Template functions only have exact matches, so they do not invoke implicit construction from compatible types.

    E.g.:

    Absolute<int> r = 3 + Absolute<int>(5)

    Works with the non-template function, because the only operator + with a RHS Absolute<T> is the operator +(Relative<T>, Absolute<T>) and Relative<int> can be constructed implicitly from int.

    In your version no operator would be matched, because templates only have exact matches.

    BTW, C2000 6.1.0 accepts my code the way I posted it here.

  • I should mention, I am building with --emit_warnings_as_errors (because people don't care about warnings). And I can turn the offendig diagnostic off with #pragma diag_suppress 1581.

    The problem with that is that C2000 6.1.0 does not know diagnostic 1581 (and thus emits an error when I add the pragma). It also doesn't know the diag_push and diag_pop pragmas, which is a nuisance, but not really a problem.
  • If your program links with no problems, then these diagnostics can be suppressed.  To suppress them only when using compiler versions 18.1.x.LTS and higher, add code like this to each header file ...

    // If compiler version is >= 18.1.0.LTS, then suppress diagnostics
    // about declaring non-template functions
    #if __TI_COMPILER_VERSION__ >= 18001000
    #pragma diag_suppress 1581
    #endif

    Thanks and regards,

    -George

  • This works for me. Still I think your policy for warnings is broken.

    Both clang and gcc impose a policy not to include warnings with false positives in -Wall. There is a separate flag that includes unreliable warnings, -Weverything.

    This is not the first time I have to turn off diagnostics, because of false positives. What I did here is a commonly known technique, see Item 46 of the Effective C++ Third Edition by Scott Meyers.
  • I understand your point of view.  Nonetheless, what you call a false positive another user may call a useful diagnostic.  That is why compiler diagnostics have different levels (remark, warning, error) and many, though not all, diagnostics can have their level changed by the user.  For details, please search the C2000 compiler manual for the sub-chapters titled Understanding Diagnostic Messages and The Diagnostic Message Pragmas.

    Thanks and regards,

    -George