Как использовать std :: transform, не нарушая Правило MISRA C ++ 2008 5-2-10?

Я получаю эти ошибки в 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 :: _ String_iterator >>,
станд :: _ String_iterator >>,
std :: _ String_iterator >>, int
(*) (int)) ‘(сравните со строкой 998, файл C: \ Program Files
(x86) \ Microsoft Visual Studio 11.0 \ VC \ include \ алгоритма) [MISRA C ++
Правила 0-1-7 и 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?

1

Решение

Я просто угадаю здесь (и я, вероятно, удалю ответ, если он не решит вашу проблему).

Попробуйте заменить строку, содержащую std::transform с этими двумя:

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

Обратите внимание на использование cbegin() а также cend() (скорее, чем begin() а также end()).

По другой теме: Вы копируете строку, переданную ConvertStdStringToBool дважды, когда ты мог сделать это только один раз. Для этого замените:

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

с

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

(Возможно, вы захотите переименовать transformValue в value после этой смены).

Обновить: Мое объяснение, почему я думаю, что это поможет.

Прежде всего, обратите внимание, что transformValue не является const, Следовательно, transformValue.begin() а также transformValue.end() вызовет эти перегрузки:

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

Отсюда статический анализатор (справедливо) заключает, что begin() а также end() может изменить состояние transformValue, В этом случае окончательное состояние transformValue может зависеть от того, какой из begin() а также end() называется первым.

Теперь, когда вы звоните cbegin() а также cend()перегрузки следующие:

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

В этом случае статический анализатор не будет выводить, что эти вызовы изменят состояние transformValue и не поднимает проблему. (Строго говоря, хотя методы const они могли бы изменить состояние, потому что могли существовать mutable члены данных в классе или методы могут использовать зло const_cast, ИМХО, не стоит винить в этом статический анализатор.)

Последнее замечание: звонок

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

неправильно. Третий аргумент должен быть не const итератор, то есть должен быть transformValue.begin() (только первые два аргумента являются c* методы).

Тем не менее, я думаю, по аналогии с рассмотренным выше, просто используя transformValue.begin() как третий аргумент будет недостаточно, и поэтому я предложил создать другую переменную (dest).

5

Другие решения

Это не отдельный ответ, скорее комментарий к ответу Кассио, но он слишком длинный для комментария.

Вот как напрямую с помощью transformValue.begin() поскольку третий параметр мог действительно легко потерпеть неудачу: в C ++ 03 (не в 11, но GCC пока не переключился), была реализована реализация std :: string с подсчетом ссылок. У libstdc ++ есть такой. С такой версией value а также transformValue поделится своим внутренним буфером.

Теперь, когда transformValue.begin() вызывается, получающийся неконстантный итератор может использоваться для изменения буфера, что было бы плохо, потому что это изменило бы value тоже. Так begin() должен освободить общий доступ к буферу, т.е. выделить уникальный буфер только для transformValue, Делать это аннулирует все существующие итераторы!

Таким образом, в C ++ 98 версия вызова cbegin а также cend:

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

у вас есть реальная зависимость заказа. Если неконстантная версия begin() вызывается до вызова const, все нормально. Но если версия const (или end()) вызывается первым, вновь возвращенные итераторы будут признаны недействительными из-за неконстантного вызова, что приведет к неопределенному поведению.

Довольно неприятно, что код все еще, вероятно, будет работать, потому что недействительные итераторы будут указывать на старый буфер, который поддерживается value, Поскольку в большинстве случаев, когда происходит такое разделение, старая копия будет длиться дольше, чем новая, это действительно неприятная ошибка спящего, которая не появится, пока

  • старая копия может исчезнуть во время операции, например, потому что старая копия уже ушла, но другая копия существует в другом потоке и может исчезнуть в любое время, или
  • код изменяется таким образом, что ожидает, что изменения, сделанные через неконстантный итератор, будут отражены в константных итераторах (которые указывают на другой буфер и, следовательно, не будут их видеть).
2

Хотя итераторы begin () и end () являются постоянными в этом вызове, средство проверки подозревает побочные эффекты от их вызова. Итак, результат может быть быть разными в зависимости от последовательности звонков. У меня была похожая проблема, и я должен был ее исправить, используя две локальные переменные, чтобы закрыть проверку.

0
По вопросам рекламы [email protected]