Проверка C на соответствие оператора равенства структуре с течением времени

#c #testing

#c #тестирование

Вопрос:

Я проголосовал за @TomalakGeretkal за хорошее замечание о by-contract; Я не принял ответ, поскольку мой вопрос заключается в том, как программно проверить функцию equals.


У меня есть POD-структура и оператор равенства, (очень) небольшая часть системы с более чем 100 инженерами.

Со временем я ожидаю, что структура будет изменена (элементы будут добавлены / удалены / переупорядочены), и я хочу написать тест, чтобы убедиться, что операция равенства проверяет каждый элемент структуры (например, обновляется по мере изменения структуры).

Как указал Томалак — комментарии и «по контракту» часто являются лучшим / единственным способом обеспечить соблюдение этого; однако в моей ситуации я ожидаю проблем и хочу изучить, есть ли какие-либо способы упреждающего отслеживания (по крайней мере, многих) изменений.

Я не даю удовлетворительного ответа — это лучшее, о чем я подумал:

 -new up two instances struct (x, y), fill each with identical non-zero data.
-check x==y
-modify x "byte by byte"
    -take ptr to be (unsigned char*)amp;x
    -iterator over ptr (for sizeof(x))
        -increment the current byte
        -check !(x==y)
        -decrement the current byte
        -check x==y
  

Тест проходит, если оператор равенства перехватил каждый байт (ПРИМЕЧАНИЕ: к этому есть предостережение — не все байты используются в представлении компиляторов x, поэтому тест должен был бы «пропустить» эти байты — например, жесткий код игнорирует байты)

Мой предлагаемый тест имеет значительные проблемы: (по крайней мере) байты ‘don’t care’ и тот факт, что увеличение одного байта типов в x может не привести к допустимому значению переменной в этой ячейке памяти.

Есть ли лучшие решения?

(Это не должно иметь значения, но я использую VS2008, rtti отключен, googletest suite)

Ответ №1:

Хотя и заманчиво сделать код «надежным» с помощью подобных самопроверок, по моему опыту, обеспечение надежности самих самопроверок — это, ну, в общем, дурацкая затея.

Сохраняйте простоту и локализуйте эффект любых изменений. Напишите комментарий в определении структуры, разъясняющий, что оператор равенства также должен быть обновлен, если структура есть; затем, если это не удается, это просто ошибка программиста.

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

Комментарии:

1. Я согласен, что «по контракту» — это лучшее, что вы часто можете сделать — в моем случае у меня ситуация, когда я ожидаю проблем и хочу изучить, есть ли какие-либо способы проявить инициативу и перехватить хотя бы большинство случаев «игнорирования комментариев программистом». Спасибо за вашу помощь!

2. @mike: Спасибо; это фраза, которую я искал. 🙂 Я бы зашел так далеко, что сказал, что часто это лучшее, что вы должны делать. Имейте в виду, это довольно субъективно.

3. Я собираюсь проголосовать за этот ответ, поскольку он правильный — я не отмечаю как принятый, поскольку мой вопрос заключается в том, как программно проверить функцию equals.

Ответ №2:

Я согласен с (и проголосовал за) Ответ Томалака. Маловероятно, что вы найдете надежное решение. Тем не менее, одним простым полуавтоматическим подходом может быть проверка ожидаемого размера в операторе равенства:

 MyStruct::operator==(const MyStruct amp;rhs) 
{
  assert(sizeof(MyStruct) == 42);  // reminder to update as new members added

  // actual functionality here ...
}
  

Таким образом, если будут добавлены какие-либо новые члены, assert будет срабатывать до тех пор, пока кто-нибудь не обновит оператор равенства. Это, конечно, не является надежным. (Переменные-члены могут быть заменены чем-то такого же размера и т.д.) Тем не менее, это относительно простой (утверждение в одну строку) способ, который позволяет легко обнаружить случай ошибки.

Комментарии:

1. это можно было бы улучшить с помощью static_assert

2. Я видел это в кодовой базе моей текущей организации (ну, во всяком случае, в версии Джастина), и, честно говоря, это работает довольно хорошо. Буквально поймал меня;)

Ответ №3:

Я уверен, что за это меня проголосуют против, но…

Как насчет функции равенства шаблона, которая принимает ссылку на параметр int и два тестируемых объекта. Функция равенства вернет bool, но увеличит ссылку на размер (int) на sizeof(T).

Затем создайте большую тестовую функцию, которая вызывает шаблон для каждого объекта и суммирует общий размер -> сравните эту сумму с размером объекта. Существование виртуальных функций / наследования и т.д. Может убить эту идею.

Комментарии:

1. Существование виртуальных функций в модуле?

2. Они исключены по определению? Я просто пытался понять области, где он может выйти из строя — может быть, виртуальный деструктор?

3. POD-структура — это агрегированный класс, который не имеет нестатических элементов данных типа non-POD-struct, non-POD-union (или массива таких типов) или ссылки, и не имеет объявленного пользователем оператора присваивания копии и не объявленного пользователем деструктора. Агрегат — это массив или класс без объявленных пользователем конструкторов, без частных или защищенных нестатических элементов данных, без базовых классов и виртуальных функций.

Ответ №4:

на самом деле это сложная проблема для правильного решения при самопроверке.

самое простое решение, которое я могу придумать, — это взять несколько шаблонных функций, которые работают с несколькими типами, выполнить необходимые преобразования, рекламные акции и сравнения, затем проверить результат во внешнем модульном тестировании. по крайней мере, вы будете знать, когда будет внесено критическое изменение.

некоторые из этих проблем легче поддерживать / проверять, используя такие подходы, как композиция, а не расширение / подклассирование.

Ответ №5:

Согласен с Томалаком и Эриком. Я использовал это для очень похожих задач.

Утверждение не работает, если не определена ОТЛАДКА, поэтому потенциально вы можете выпустить неправильный код. Эти тесты не всегда будут работать надежно. Если структура содержит битовые поля или вставлены элементы, которые занимают мало места из-за выравнивания компилятором по границам слов, размер не изменится. По этой причине они предлагают ограниченную ценность. например

 struct MyStruct {   
    char a ;
    ulong l ;
}
  

изменено на

 struct MyStruct {   
    char a ;
    char b ;  
    ulong l ;
}
  

Обе структуры имеют размер 8 байт (в 32-разрядном Linux x86)