Contract assertions (since C++26)
Контрактные утверждения позволяют программисту задавать свойства состояния программы, которые должны выполняться в определенных точках во время выполнения.
Содержание |
Объяснение
Контрактные утверждения
вводятся с помощью
спецификаторов контрактов функций
и
contract_assert
выражений. Каждое контрактное утверждение имеет
предикат
, который является выражением типа
bool
.
Оценка контрактных утверждений
Оценка утверждения контракта использует одну из следующих семантик оценки:
| Семантика оценки | Является проверяющей семантикой | Является завершающей семантикой |
|---|---|---|
| ignore | ||
| observe | Да | |
| enforce | Да | Да |
| quick-enforce | Да | Да |
Реализация определяет, какая семантика оценки используется для любого заданного вычисления утверждения контракта. Семантика оценки может различаться для разных вычислений одного и того же утверждения контракта, включая вычисления во время константной оценки.
Если используется семантика "ignore", оценка утверждения контракта не оказывает никакого эффекта.
Если используется проверяющая семантика, вычисление
E
утверждения контракта определяет значение предиката. Не указано, вычисляется ли предикат. Если выполняется любое из следующих условий, происходит
нарушение контракта
:
- Значение, которое получилось бы при вычислении предиката, равно false .
- Вычисление предиката завершается через исключение.
- Вычисление предиката выполняется в контексте, который является явно константно-вычисляемым , и предикат не является ядром константного выражения .
Существует
наблюдаемая точка контроля
CP
, которая происходит до
E
, такая что
любая другая операция
OP
, которая происходит до
A
, также происходит до
CP
.
int num = 0; void f() pre((num++, false)); f(); // Инкремент "num" может не произойти, даже если используется проверяющая семантика
Обработка нарушений контракта
Если нарушение контракта происходит в контексте, который является явно константно-вычисляемым:
- Если семантика вычисления — «наблюдение», создается диагностическое сообщение.
- Если семантика вычисления является завершающей семантикой, программа считается некорректной.
Если нарушение контракта происходит в контексте, который не является явно константно-вычисляемым:
- Если семантика оценки — «quick-enforce», программа завершается по контракту.
-
Если семантика оценки — «enforce» или «observe», вызывается обработчик нарушения контракта со ссылкой на lvalue объекта
obj
типа
const
std
::
contracts
::
contract_violation
, содержащего информацию о нарушении контракта.
- Память для obj выделяется неопределённым образом, но глобальная функция выделения памяти не будет вызвана.
- Время жизни obj сохраняется на протяжении всего вызова обработчика нарушения контракта.
Программы с завершенным контрактом
Когда программа contract-terminated , определяется реализацией (в зависимости от контекста), будет ли
- std::terminate вызывается,
- std::abort вызывается, или
- выполнение завершается (дальнейшие шаги выполнения не происходят).
Обработчик нарушения контракта
Обработчик нарушения контракта программы — это функция с именем :: handle_contract_violation :
|
void
handle_contract_violation
(
std
::
contracts
::
contract_violation
)
;
|
(начиная с C++26)
(опционально noexcept) |
|
Определение обработчика нарушения контракта, называемого обработчиком нарушения контракта по умолчанию , предоставляется реализацией (вместо заголовочного файла стандартной библиотеки).
Является ли обработчик нарушения контракта заменяемым — определяется реализацией. Если обработчик нарушения контракта не является заменяемым, объявление заменяющей функции для обработчика нарушения контракта является некорректным, диагностика не требуется.
Когда обработчик нарушения контракта возвращается нормально:
- Если семантика оценки — «observe», поток управления продолжается нормально после точки оценки утверждения контракта.
- Если семантика оценки — «enforce», программа завершается по контракту.
Существует
наблюдаемая точка контроля
CP
, которая возникает после нормального возврата обработчика нарушения контракта, так что любая другая операция
OP
, происходящая после возврата обработчика нарушения контракта, также происходит после
CP
.
Обработка исключений из утверждений
Если нарушение контракта произошло из-за того, что вычисление предиката завершилось с исключением и семантика вычисления имеет значение "observe" или "enforce", обработчик нарушения контракта вызывается из активного неявного handler для этого исключения.
Когда обработчик нарушения контракта возвращается нормально:
- Если семантика оценки — «observe», неявный обработчик больше не считается активным.
- Если семантика оценки — «enforce», неявный обработчик остается активным при завершении контракта.
Текущее исключение может быть проверено или повторно выброшено в обработчике нарушения контракта с помощью std::current_exception() .
Выполнять последовательно
Для
последовательной оценки
списка
R
контрактных утверждений:
S
, удовлетворяющий всем следующим условиям:
-
Все элементы
Rсодержатся вS. -
Каждый элемент
Rможет повторяться вSопределённое реализацией количество раз. -
Если контрактное утверждение
Aпредшествует другому контрактному утверждениюBвR, то первое вхождениеAпредшествует первому вхождениюBвS.
S
таким образом, что если контрактное утверждение
A
предшествует контрактному утверждению
B
в
S
, тогда вычисление
A
упорядочено перед
вычислением
B
.
void f(int i) { contract_assert(i > 0); // #1 contract_assert(i < 10); // #2 // допустимая последовательность вычислений: #1 #2 (без повторений) // допустимая последовательность вычислений: #1 #1 #2 #2 (повторение последовательно) // допустимая последовательность вычислений: #1 #2 #1 #2 (повторение попеременно) // допустимая последовательность вычислений: #1 #2 #2 #1 (вторые вхождения могут менять порядок) // недопустимая последовательность вычислений: #2 #1 (первые вхождения не могут менять порядок) }
Примечания
Диапазон и гибкость доступных вариантов семантики вычислений зависят от реализации и не обязательно должны допускать все четыре варианта семантики вычислений как возможные.
Различные семантики оценки, выбранные для одного и того же контрактного утверждения в разных единицах трансляции, могут привести к нарушению правила одного определения когда контрактное утверждение имеет побочные эффекты, изменяющие значение, производимое константным выражением:
constexpr int f(int i) { contract_assert((++const_cast<int&>(i), true)); return i; } inline void g() { int a[f(1)]; // размер зависит от семантики вычисления contract_assert выше }
Если значение, которое получится в результате вычисления предиката, равно true , нарушение контракта не происходит, и управление продолжается нормально после точки вычисления утверждения контракта.
Если вычисление предиката завершается посредством нелокальных переходов или завершения программы, нарушение контракта также не происходит.
Рекомендуется стандартом C++, что обработчик нарушений контракта по умолчанию должен выдавать диагностический вывод, который соответствующим образом форматирует наиболее релевантное содержимое аргумента (с ограничением частоты для потенциально повторяющихся нарушений наблюдаемых утверждений контракта), и затем возвращаться нормально.
| Макрос тестирования возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_contracts
|
202502L
|
(C++26) | Контракты |
Ключевые слова
contract_assert , pre , post
Статус поддержки
|
Функция C++26
|
Документ(ы)
|
GCC
|
Clang
|
MSVC
|
Apple Clang
|
EDG eccp
|
Intel C++
|
Nvidia HPC C++ (ex PGI)*
|
Nvidia nvcc
|
Cray
|
|---|---|---|---|---|---|---|---|---|---|---|
| Контракты ( FTM ) * | P2900R14 |
Смотрите также
contract_assert
statement
(C++26)
|
проверяет внутреннее условие во время выполнения |
| function contract specifiers (C++26) | определяет предусловия ( pre ) и постусловия ( post ) |