Утверждения контрактов (начиная с 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(); // Increment of “num” might not occur, even if a checking semantic is used
Обработка нарушений контрактов
Если нарушение контракта происходит в контексте, который явно вычисляется во время константного вычисления:
- Если семантика вычисления — «observe», выдаётся диагностическое сообщение.
- Если семантика вычисления является завершающей, программа некорректна.
Если нарушение контракта происходит в контексте, который не вычисляется явно во время константного вычисления:
- Если семантика вычисления — «quick-enforce», выполнение программы завершается вследствие нарушения контракта.
- Если семантика вычисления — «enforce» или «observe», вызывается обработчик нарушения контракта с lvalue, ссылающимся на объект
objтипа conststd::contracts::contract_violation, содержащий сведения о нарушении контракта.- Память для
objвыделяется неопределённым образом, но глобальная функция выделения памяти вызвана не будет. - Время жизни
objпродолжается в течение всего вызова обработчика нарушения контракта.
- Память для
Программы, выполнение которых завершено вследствие нарушения контракта
Когда выполнение программы завершается вследствие нарушения контракта , в зависимости от контекста реализация определяет, будет ли
-
вызвана
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», обработчик нарушения контракта вызывается внутри активного неявного обработчика этого исключения.
Когда обработчик нарушения контракта возвращает управление обычным образом:
- Если семантика вычисления — «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
// valid sequence of evaluations: #1 #2 (no repeat)
// valid sequence of evaluations: #1 #1 #2 #2 (repeat in sequence)
// valid sequence of evaluations: #1 #2 #1 #2 (repeat alternatively)
// valid sequence of evaluations: #1 #2 #2 #1 (second occurences can switch order)
// invalid sequence of evaluations: #2 #1 (first occurences cannot switch)
}
Примечания
Диапазон и гибкость доступных вариантов семантики вычисления зависят от реализации, и не обязательно поддерживаются все четыре возможные семантики вычисления.
Выбор разных семантик вычисления для одного и того же утверждения контракта в разных единицах трансляции может привести к нарушению правила одного определения, если утверждение контракта имеет побочные эффекты, изменяющие значение, получаемое при вычислении константного выражения:
constexpr int f(int i)
{
contract_assert((++const_cast<int&>(i), true));
return i;
}
inline void g()
{
int a[f(1)]; // size dependent on the evaluation semantic of contract_assert above
}
Если значение, которое получилось бы в результате вычисления предиката, равно true, нарушения контракта не происходит, и поток управления продолжается обычным образом после точки вычисления утверждения контракта.
Если вычисление предиката завершается с помощью нелокального перехода или завершения программы, нарушения контракта также не происходит.
Стандарт C++ рекомендует, чтобы обработчик нарушения контракта по умолчанию выводил диагностические данные с подходящим форматированием наиболее важного содержимого аргумента (ограничивая частоту вывода для потенциально повторяющихся нарушений наблюдаемых утверждений контрактов), а затем возвращал управление обычным образом.
| Макрос проверки поддержки возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_contracts | 202502L | (C++26) | Контракты |
Ключевые слова
Статус поддержки
| Возможность C++26 | Документ(ы) |
GCC |
Clang |
MSVC |
Apple Clang |
EDG eccp |
Intel C++ |
Nvidia HPC C++ (ранее PGI)* |
Nvidia nvcc |
Cray |
|---|---|---|---|---|---|---|---|---|---|---|
| Контракты (FTM)* | P2900R14 | 16 |
См. также
Инструкция contract_assert (C++26) | проверяет внутреннее условие во время выполнения |
| спецификаторы контрактов функций (C++26) | задают предусловия (pre) и постусловия (post) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/contracts