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

Я читал о контрактах в Мысли о 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, был создан.


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


Ответы (3)


Насколько я прочитал из этого документа: 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 документа, упомянутого в начале этого поста, говорится о возможностях отключения контрактов в зависимости от вашей конфигурации сборки, как и в старых простых утверждениях.

person KABoissonneault    schedule 09.07.2015
comment
Спасибо за ответ. Поэтому, если я буду следовать, использование утверждений должно быть устаревшим, и вместо этого мы должны использовать контракты. Не могли бы вы привести пример того, что ваше сообщение может изменить часть исключений? - person coincoin; 09.07.2015
comment
@coincoin Отредактировано для включения примера - person KABoissonneault; 09.07.2015
comment
Утверждения @coincoin не будут объявлены устаревшими, поскольку контракты не подходят для тестирования, - person Pavel Oganesyan; 09.07.2015
comment
AFAIK пред- и пост-условия не имеют ничего общего с инвариантами. Инвариант — это свойство, которое сохраняется на всех итерациях цикла. Вы можете применить это определение к вызову функции: инвариант для функции f — это свойство, сохраняемое вызовом f. Однако это не то же самое, что предварительные и последующие условия. - person Bakuriu; 09.07.2015
comment
@Bakuriu Википедия документирует Инвариант и Инвариант цикла как две разные вещи. Хотя в статье об инварианте не упоминаются предварительные или постусловия, в ней упоминаются инварианты как логические утверждения. Конечно, Википедия не является единственной авторитетной базой знаний по терминам компьютерных наук, но я предполагаю, что на самом деле существуют две разные концепции, известные как инварианты, и та, которую я упомянул, является лишь одной из них. - person KABoissonneault; 09.07.2015
comment
Обратите внимание, что в контрактах С++ 20, если вы выдаете исключение, любое условие публикации не проверяется. Только когда вы возвращаетесь нормально, то есть с помощью return statement;. - person Rakete1111; 30.01.2019

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

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

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

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

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

и пример:

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

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

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

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

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

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

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

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

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

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

person Pavel Oganesyan    schedule 09.07.2015
comment
Спасибо за ваш ответ. Итак, вы говорите, что в зависимости от вашего контекста использование утверждений и исключений все еще может понадобиться? Можно ли привести несколько примеров, когда использование утверждений, исключений или контрактов кажется более законным, чем другие? - person coincoin; 09.07.2015
comment
@coincoin Вы определенно захотите использовать assert для оценки пост-условий функций, которые не используют контракты (например, это никогда не должно возвращать значение null, но на всякий случай...). Исключения, на мой взгляд, представляют собой систему, полностью ортогональную Контрактам/утверждениям; исключения следует использовать не для нарушения контракта, а для исключительных ситуаций, таких как ошибка сокета или ошибка обработки файла. Поэтому их все же следует использовать, но, на мой взгляд, не за нарушение предусловия! - person KABoissonneault; 09.07.2015
comment
@coincoin Согласитесь с KABoissonneault по поводу использования исключений для проблем с кодом, а также для ответа добавлены некоторые варианты использования. - person Pavel Oganesyan; 09.07.2015

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

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

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

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

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

person Fabio Fracassi    schedule 09.07.2015
comment
Спасибо. Так что, я думаю, нам придется подождать некоторое время, чтобы прояснить некоторые моменты. - person coincoin; 09.07.2015
comment
Да, или начать вмешиваться и формировать его. - person Fabio Fracassi; 10.07.2015