Search code examples
c++misra

How to use std::transform without violating MISRA C++ 2008 Advisory Rule 5-2-10?


I get these error in PC-Lint (au-misra-cpp.lnt):

ConverterUtil.cpp(90): error 864: (Info -- Expression involving variable 'transformValue' possibly depends on order of evaluation [MISRA C++ Rule 5-2-10])

ConverterUtil.cpp(90): error 864: (Info -- Expression involving variable 'transformValue' possibly depends on order of evaluation [MISRA C++ Rule 5-2-10])

ConverterUtil.cpp(90): error 534: (Warning -- Ignoring return value of function 'std::transform(std::_String_iterator>>, std::_String_iterator>>, std::_String_iterator>>, int (*)(int))' (compare with line 998, file C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\algorithm) [MISRA C++ Rules 0-1-7 and 8-4-6], [MISRA C++ Rule 0-3-2])

On this code:

/**Conversion from std::string to bool*/
bool ConverterUtil::ConvertStdStringToBool(const std::string value)
{
    std::string transformValue = value;
    bool retValue = false;

    std::transform(transformValue.begin(), transformValue.end(), transformValue.begin(), &::tolower);


    if(transformValue == std::string(static_cast<const char *>("true")))
    {
        retValue = true;
    }

    return retValue;
}

I guessed it didn't like the fact that I use the same std::string as input and output in the transform, but using an other string as output gives the same error.

Is it possible to make the std::transform MISRA compliant?


Solution

  • I'm just guessing here (and I'll probably remove the answer if it doesn't solve your problem).

    Try to replace the line containning std::transform with these two:

    auto dest = transformValue.begin();
    std::transform(transformValue.cbegin(), transformValue.cend(), dest, &::tolower);
    

    Notice the use of cbegin() and cend() (rather than begin() and end()).

    On another topic: You're copying the string passed to ConvertStdStringToBool twice when you could do it just once. To do this, replace:

    bool ConverterUtil::ConvertStdStringToBool(const std::string value)
    {
        std::string transformValue = value;
    

    with

    bool ConverterUtil::ConvertStdStringToBool(std::string transformValue)
    {
    

    (You might want to rename transformValue to value after this change).

    Update: My explanation why I think it's going to help.

    First of all, notice that transformValue is non const. Hence, transformValue.begin() and transformValue.end() will call these overloads:

    iterator begin(); // non const overload
    iterator end();   // non const overload
    

    Hence the static analyser (rightly) concludes that begin() and end() might change the state of transformValue. In this case, the final state of transformValue might depend on which of begin() and end() is called first.

    Now, when you call cbegin() and cend(), the overloads are these:

    const_iterator cbegin() const; // notice the const 
    const_iterator cend() const;   // notice the const
    

    In this case, the static analyser will not deduce that these calls will change the state of transformValue and doesn't raise the issue. (Strictly speaking even though the methods are const they could change the state because there could exist mutable data members in the class or the methods could use an evil const_cast. IMHO, the static analyser shouldn't be blamed for that.)

    Final remark: The call

    std::transform(transformValue.cbegin(), transformValue.cend(), transformValue.cbegin(), &::tolower);
                                                                                  ^^^^^^
    

    is wrong. The third argument must be the non const iterator, that is, it must be transformValue.begin() (only the first two arguments are the c* methods).

    However, I guess, for a similar reasoning as above, just using transformValue.begin() as the third argument won't be enough and that's why I suggested creating another variable (dest).