1

PC-Lint (au-misra-cpp.lnt) で次のエラーが発生します。

ConverterUtil.cpp(90): エラー 864: (情報 -- 変数 'transformValue' を含む式は、評価の順序に依存する可能性があります [ MISRA C++ ルール 5-2-10 ])

ConverterUtil.cpp(90): エラー 864: (情報 -- 変数 'transformValue' を含む式は、評価の順序に依存する可能性があります [ MISRA C++ ルール 5-2-10 ])

ConverterUtil.cpp(90): エラー 534: (警告 -- 関数 'std::transform(std::_String_iterator>>、std::_String_iterator>>、std::_String_iterator>>、int (*) の戻り値を無視しています(int))' (998 行目、ファイル C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\algorithm と比較) [MISRA C++ Rules 0-1-7 and 8-4-6], [ MISRA C++ ルール 0-3-2])

このコードで:

/**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;
}

変換で同じ std::string を入力と出力として使用するという事実が気に入らなかったと思いますが、出力として別の文字列を使用すると同じエラーが発生します。

std::transform を MISRA に準拠させることは可能ですか?

4

3 に答える 3

5

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).

于 2013-08-09T10:55:57.753 に答える
2

これは個別の回答ではなく、Cassio の回答に対するコメントに似ていますが、コメントするには長すぎます。

3 番目のパラメーターとして直接使用するtransformValue.begin()と、実際には簡単に失敗する可能性があります。C++03 (11 ではなく、GCC はこれまで切り替えていません) では、std::string の参照カウント実装が許可されていました。libstdc++ には 1 つがあります。このようなバージョンでvaluetransformValue、内部バッファーを共有します。

transformValue.begin()が呼び出されると、結果の非定数イテレータを使用してバッファを変更できますが、バッファも変更されるため問題がありますvalue。そのため、バッファの共有を解除する必要があります。つまりbegin()、 のためだけに一意のバッファを割り当てtransformValueます。これを行うと、既存のイテレータがすべて無効になります!

したがって、C++98 バージョンの and の呼び出しでは、次のようcbeginになりcendます。

const std::string& constValue = transformValue;
std::transform(constValue.begin(), constValue.end(),
               transformValue.begin(), &::tolower);

あなたは本当の順序依存性を持っています。const が呼び出される前に非 const バージョンbegin()が呼び出された場合、すべて問題ありません。ただし、const バージョン (またはend()) が最初に呼び出された場合、新しく返された反復子は非 const 呼び出しによって無効になり、未定義の動作が発生します。

厄介なことに、無効化されたイテレータは古いバッファを指し、value. このような非共有が発生するほとんどの場合、古いコピーは新しいコピーよりも長く存在するため、これは非常に厄介なスリーパー バグであり、次のいずれかになるまで表面化しません。

  • 古いコピーは操作中に消える可能性があります。たとえば、古いコピーはすでに消えているが、別のコピーが別のスレッドに存在し、いつでも消える可能性があるためです。
  • コードは、非定数反復子を介して行われた変更が定数反復子に反映されることを期待する方法で変更されます (別のバッファーを指すため、それらは表示されません)。
于 2013-08-09T12:14:40.490 に答える