Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Что такое контракты (как предложено для С++ 17)?

Автор вопроса: coincoinОтветов: 3

Я читал о контрактах в Мысли о C++ 17 Б. Страуструпа и помог провести небольшую презентацию, рассказав о них, но я не уверен, что действительно их понял.

Итак, у меня есть несколько вопросов, и если можно проиллюстрировать их некоторыми примерами:

  • Являются ли контракты просто лучшей заменой классического assert(), и следует ли их использовать вместе? Какие контракты действительно сформулированы простым языком для разработчика программного обеспечения?

  • Повлияют ли контракты на то, как мы обрабатываем исключения? Если да, то как нам использовать исключения и контракты?

  • Будет ли использование контрактов означать накладные расходы во время выполнения? Будет ли нам разрешено деактивировать их в коде выпуска?

Из предложения N4415:

Контракт предварительного условия оператора индексации класса Vector можно записать так:
T& operator[](size_t i) [[expects: i < size()]];

Точно так же контракт пост-условия для конструктора класса ArrayView может быть выражен как: ArrayView(const vector<T>& v) [[ensures: data() == v.data()]];

Благодаря комментарию @Keith Thompson:

Контракты не вошли в C++20. Новая исследовательская группа, SG21, был создан.

  • Они похожи на проверки отладки, которые вы можете включить или отключить в зависимости от уровня проверки, предоставленного компилятору. Любые проверки отключенного уровня не влекут за собой затраты времени выполнения. Почему бы вам не прочитать предложение для них? RamblingMad 09.07.2015
  • @BasileStarynkevitch, этот вопрос плохо подходит для программистов - он будет быстро отклонен и закрыт там, см. meta.programmers.stackexchange.com/questions/6483/ Рекомендуемое чтение: Что происходит на Programmers.SE? Руководство по переполнению стека gnat 09.07.2015
  • Всем, кто читает это после 2017 года: в конце концов, контракты не вошли в C++17. Может С++20? einpoklum 05.04.2018
  • @einpoklum: Всем, кто читает это после 2019 года: Контракты не вошли в C++20. Создана новая исследовательская группа SG21. herbsutter.com/2019/07 /20/ Keith Thompson 20.07.2019

c++c++17assertiondesign-by-contract

Ответы

Ответ 1 · рейтинг 24 · KABoissonneault

Насколько я прочитал из этого документа: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4415.pdf

Контракты делают то, что assert годами пытался сделать примитивным способом. Это и документация, и подтверждение во время выполнения того, как вызывающая сторона должна вызывать функцию и в каком состоянии вызывающая сторона может ожидать, что код будет находиться после возврата функции. Их обычно называют предусловиями и постусловиями, или инвариантами.

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

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

Пример:

class Data;
class MyVector {
public:
    void MyVector::push_back(Elem e) [[ensures: data != nullptr]]
    {
        if(size >= capacity)
        {
            Data* p = data;
            data = nullptr; // Just for the sake of the example...
            data = new Data[capacity*2]; // Might throw an exception
            // Copy p into data and delete p
        }
        // Add the element to the end
    }
private:
     Data* data;
     // other data
};

В этом примере здесь, если конструктор new или Data выдает исключение, ваше пост-условие нарушается. Это означает, что вы должны изменить весь такой код, чтобы убедиться, что ваш Контракт никогда не нарушается!

Конечно, как и в случае assert, контракты могут включать накладные расходы во время выполнения. Разница, однако, заключается в том, что, поскольку контракты могут быть помещены как часть объявления функции, компилятор может выполнять лучшую оптимизацию, например, оценивать условия на сайте вызывающей стороны или даже оценивать их во время компиляции. В разделе 1.5 документа, упомянутого в начале этого поста, говорится о возможностях отключения контрактов в зависимости от вашей конфигурации сборки, как и в старых простых утверждениях.

Ответ 2 · рейтинг 10 · Pavel Oganesyan

Я начал с ссылки из предоставлен оригинальный документ OP. Есть некоторые ответы, я полагаю. Настоятельно рекомендую начать с этой бумаги. Вот версия TL&DR:

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

О ваших вопросах:

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

...выражение контракта должно логически быть частью объявления операции.

и пример:

T& operator[](size_t i) [[expects: i < size()]];

На мой взгляд, это просто красиво и читабельно.

  • В некоторых случаях контракты могут заменить исключения:

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

В контрактах с предварительным условием по-прежнему можно использовать исключения, поскольку дальнейшее поведение после отказа контракта с предварительным условием не гарантируется.

  • Накладные расходы можно уменьшить, включив/отключив проверку контрактов для случаев: использовать все, использовать не, только предварительное условие, только постусловие. Включенные контракты определенно добавят некоторые накладные расходы, как и любой тип проверок.

Некоторые варианты использования (как я могу предположить, хотя я даже не близок к разработке дизайна контрактов)

  1. Контракты - в случае обычного assert(), так как контракты более читабельны и могут быть оптимизированы во время компиляции.
  2. Утверждения - в модульных тестах, средах тестирования и т. д.
  3. Исключения - можно использовать с предварительно обусловленными контрактами, как указано в статье:

Предварительное условие операции оценивается перед любым другим оператором в теле функции. Если результат верен, то обычное управление выполнением продолжается с первого оператора в теле функции. В противном случае дальнейшее выполнение не гарантируется: либо программа прерывается, либо выдает исключение, либо, если ей разрешено продолжить, поведение не определено.

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

Ответ 3 · рейтинг 4 · Fabio Fracassi

Нелегко ответить на ваши вопросы, кроме: Это зависит. Это связано с тем, что еще не ясно, какими именно будут контракты. Сейчас есть несколько предложений и идей:

  • n4378 Lakos et al. в основном предлагает стандартизировать сложный инструментарий утверждений. Контракты проверяются внутри реализации функций, предусмотрено 3 различных уровня утверждений для контроля количества проверок во время выполнения, а обработка нарушений утверждений может быть настроена.

  • n4415 душ Рейс и др. и n4435 Brown довольно похожи и предлагают синтаксис на основе атрибутов для определения предварительных и последующих условий в интерфейсах функций. Они не вдаются в подробности того, какой уровень контроля они дают над проверками во время выполнения и поведением при нарушениях.

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

Ваш вопрос об исключениях особенно сложен, потому что взаимодействие между обработкой нарушения контракта и исключениями неясно (например, может ли обработчик нарушения контракта бросить (полезно в тестовых средах)? Что, если функция noexcept(true)?).

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы