Подтвердить что ты не робот

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

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

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

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

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

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

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

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

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

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

Контракты не попали в С++ 20. Была создана новая учебная группа SG21.

4b9b3361

Ответ 1

Насколько я прочитал из этого документа: 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

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

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

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

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

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

и пример:

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

По-моему, это просто приятно и читаемо.

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

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

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

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

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

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

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

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

Ответ 3

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

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

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

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

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