Как проверить и обработать нарушения предварительных условий? - PullRequest
1 голос
/ 03 мая 2019

C ++ 20 предоставляет несколько удивительных новых функций, связанных с контрактами - которые для шаблонов сделают жизнь намного лучше - где ограничения вокруг типов или другие требования времени компиляции могут быть встроены в определение шаблона и обеспечены приличной диагностикой с помощьюкомпилятор.Да!

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

https://en.cppreference.com/w/cpp/language/attributes/contract

Программа может бытьпереводится с одним из двух режимов продолжения нарушения:

off (по умолчанию, если не выбран режим продолжения): после завершения выполнения обработчика нарушения вызывается std :: terminate;on: после выполнения обработчика нарушения выполнение продолжается в обычном режиме.Реализациям рекомендуется не предоставлять какой-либо программный способ запрашивать, устанавливать или изменять уровень сборки или устанавливать или изменять обработчик нарушений.

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

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

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

Но стремление сделать предусловия просто завершается и не может перехватить такую ​​ошибку и повторить попыткуоперация?Это звучит как серьезное ухудшение набора функций для меня.Правда, есть домены, в которых это именно желательно.Сбой быстро, сбой рано, и для предусловий или постусловий проблема заключается в способе написания кода, и пользователь не может исправить ситуацию.

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

Дискуссия Херба Саттера в ACCU, похоже, полностью совпадает с точкой зрения, что нарушения предусловий и постусловий просто отменяют условия:

https://www.youtube.com/watch?v=os7cqJ5qlzo

Я ищу то, что думают другие профессионалы C ++Исходя из вашего опыта, кодирование сообщает вам?

Я знаю, что многие проекты запрещают исключения.Если вы работаете над одним из таких проектов, значит ли это, что вы пишете свой код, который просто завершается всякий раз, когда происходит неправильный ввод?Или вы отказываетесь использовать состояния ошибки для некоторой родительской кодовой точки, которая может каким-либо образом продолжаться?

Может быть, более конкретно - возможно, я неправильно понимаю природу намерений среды выполнения C ++ 20Контракты предназначены для?

Пожалуйста, держите это в тайне - и если вы предлагаете закрыть это - возможно, вы могли бы быть так любезны, чтобы указать на более подходящий форум для этого обсуждения?

Большинствов общем, я пытаюсь ответить, к моему удовлетворению:

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

1 Ответ

13 голосов
/ 03 мая 2019

Это действительно сводится к этому вопросу: что вы имеете в виду , когда произносите слово «предварительное условие»?

То, как вы, кажется, используете это слово, означает «а»вещь, которая проверяется при вызове этой функции. "То, как Херб, стандарт C ++ и, следовательно, система контрактов C ++, означают, что это «вещь, которая должна быть верной для правильного выполнения этой функции, и если она не верна, то вы сделали неправильную вещь и мир разрушен . "

И эта точка зрения действительно сводится к тому, что означает" контракт ".Рассмотрим vector::operator[] против vector::at().at не имеет предварительного условия в стандарте C ++;выдает, если индекс выходит за пределы допустимого диапазона.Это значит, что часть интерфейса из at позволяет передавать его вне допустимых значений, и он будет реагировать ожидаемым, предсказуемым образом.

дело не в operator[].Это , а не часть интерфейса этой функции, которую вы можете передавать за пределы диапазона индексов.Таким образом, у него есть предварительное условие, что индекс не выходит за пределы допустимого диапазона.Если вы передадите ему индекс вне допустимого диапазона, вы получите неопределенное поведение .

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

int main()
{
    std::vector<int> ivec = {10, 209, 184, 96};

    int ix;
    std::cin >> ix;

    //case 1:
    try
    {
        std::cout << ivec.at(ix);
    }
    catch(const std::exception &)
    {
        std::cout << "Invalid input!\n";
    }

    //case 2:
    if(0 <= ix && ix < ivec.size())
        std::cout << ivec[ix];
    else
        std::cout << "Invalid Input!\n";

    //case 3:
    std::cout << ivec[ix];

    return 0;
}

В случае 1, мы видимиспользование at.В случае неправильного ввода мы отлавливаем исключение и обрабатываем его.

В случае 2 мы видим использование operator[].Мы проверяем, находится ли ввод в допустимом диапазоне, и, если это так, вызывают operator[].

В случае 3 мы видим ... ошибку в нашем коде .Зачем?Потому что никто не продезинфицировал ввод.Кто-то должен был, и предварительное условие operator[] говорит, что это работа вызывающего абонента.Вызывающая сторона не может очистить свои входные данные и, таким образом, представляет неработающий код.

Вот что означает заключение контракта: если код нарушает контракт, то ошибка кода заключается в его нарушении.

Но, как мы видим, контракт, по-видимому, является фундаментальной частью интерфейса функции.Если так, то почему эта часть интерфейса находится в тексте стандарта, а не в видимом объявлении функции, где люди могут его видеть?Это право - весь смысл языковой функции контрактов: позволить пользователям выражать этот специфический вид языка в языке.

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

Если это условие ошибки, то вам следует использовать предпочитаемый механизм ошибок, а не контракт.

...