assert () vs forcece (): что выбрать? - PullRequest
4 голосов
/ 25 февраля 2011

Мне трудно выбрать, должен ли я "принудительно применять" условие или "утверждать" условие в D. (Хотя это не зависит от языка.)

Теоретически, я знаю, что выиспользуйте утверждения для поиска ошибок, и вы применяете другие условия для проверки нетипичных условий.Например, вы можете сказать assert(count >= 0) в качестве аргумента для вашего метода, потому что это указывает на то, что в вызывающей стороне есть ошибка, и вы скажете enforce(isNetworkConnected), потому что это не ошибка, это просто то, что, как вы предполагаете, можеточень хорошо, чтобы не быть правдой в законной ситуации вне вашего контроля.

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

Так что я думаю, что знаю, что они должны означать.Но теория легче, чем практика, и мне трудно применять эти концепции.

Примите во внимание следующее:

Я создаю диапазон (похожий на итератор), который выполняет итерациюпо двум другим диапазонам, и добавляет результаты.(Для функциональных программистов: я знаю, что вместо этого могу использовать map!("a + b"), но пока игнорирую это, поскольку это не иллюстрирует вопрос.) Итак, у меня есть код, который выглядит следующим образом в псевдокоде:

void add(Range range1, Range range2)
{
    Range result;
    while (!range1.empty)
    {
        assert(!range2.empty);   //Should this be an assertion or enforcement?
        result += range1.front + range2.front;
        range1.popFront();
        range2.popFront();
    }
}

Должно ли это быть утверждением или принуждением?(Является ли вызывающая сторона виновной в том, что диапазоны не очищаются в одно и то же время? Возможно, он не контролирует, откуда произошел диапазон - он мог прийти от пользователя - но, опять же, он по-прежнему выглядит какошибка, не так ли?)

Или вот еще один пример псевдокода:

uint getFileSize(string path)
{
    HANDLE hFile = CreateFile(path, ...);
    assert(hFile != INVALID_HANDLE_VALUE); //Assertion or enforcement?
    return GetFileSize(hFile); //and close the handle, obviously
}
...

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

Спасибо!

Ответы [ 3 ]

1 голос
/ 25 февраля 2011

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

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

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

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

Называя ее ошибкой во время выполнения, выражается, что «разумно» (если она ненормальная) передавать списки различной длины, за исключением того, что это произошло в этом случае. Поэтому я думаю, что это скорее принуждение, чем утверждение.

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

Другой пример, который нужно рассмотреть, это operator[] против at() для вектора C ++. at() выдает out_of_range, логическую ошибку не потому, что вызывающий абонент может не захотеть восстановиться, или потому, что вы должны быть каким-то тупиком, чтобы совершить ошибку при доступе к массиву вне диапазона, используя at(), но поскольку ошибки можно избежать, если вызывающая сторона хочет, чтобы она была - вы всегда можете проверить size() перед доступом, если у вас нет другого способа узнать, хорош ваш индекс или нет. И поэтому operator[] вообще не гарантирует никаких проверок, и во имя эффективности доступ вне диапазона имеет неопределенное поведение.

1 голос
/ 18 июля 2018

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

Надлежащим средством, чтобы избежать неправильных входных данных функции, являются контракты, поэтому в примере функции должен быть входной контракт, который проверяет, что range2 по крайней мере равенrange1.Утверждение внутри реализации могло бы тогда оставаться на месте.Особенно в более длинных, более сложных реализациях такое утверждение может оказаться понятным.

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

0 голосов
/ 25 февраля 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...