Я понимаю, что, хотя у вопроса нет языковой метки, он, вероятно, косвенно говорит о «кофейных языках». Но для полноты картины я хотел бы упомянуть несколько расходящийся кажущийся консенсус в мире C ++.
Программисты на C ++ обычно интересуются тремя вещами:
- Будет ли это иметь нулевые издержки в оптимизированных сборках? (То есть можно ли его «составить»?)
- Могу ли я использовать его для отладки прямо в том месте, где была обнаружена ошибка?
- Могу ли я использовать его для сообщения о проблемах от объявленных функций
noexcept
?
В прошлом я подошел к первой проблеме, написав такой код
int
factorial(const int n)
{
if (CHECK_ARGS)
{
if (n < 0)
throw std::invalid_argument {"n < 0"};
}
int fac = 1;
for (int i = 2; i <= n; ++i)
fac *= i;
return fac;
}
где CHECK_ARGS
находится #define
d к постоянная времени компиляции поэтому компилятор может полностью устранить все аргумент коды проверки в оптимизированной сборке. (Я не говорю, что компиляция проверок - это вообще хорошая вещь, но я верю, что у пользователя должна быть возможность их компилировать.)
Мне все еще нравится в этом решении то, что код проверки аргументов хорошо виден, сгруппированный в if
. Тем не менее, второй и третий вопрос не решаются этим. Поэтому теперь я снова склоняюсь к использованию assert
макроса для проверки аргументов.
Стандарты кодирования Boost согласны с этим:
Как насчет ошибок программиста?
Как разработчик, если я нарушил предварительное условие библиотеки, которую я использую, я не хочу раскручивать стек. То, что я хочу, - это дамп ядра или его эквивалент - способ проверки состояния программы в точном месте, где была обнаружена проблема. Это обычно означает assert()
или что-то подобное.
Был очень интересный доклад Джона Лакоса на CppCon'14 под названием « Защитное программирование сделано правильно» ( часть 1 , часть 2 ). В первой части своего выступления он обсуждает теорию контрактов и неопределенного поведения. Во второй части он представляет то, что я считаю очень хорошим предложением для систематической проверки аргументов. По сути, он предлагает макросы утверждений, которые позволяют пользователю выбирать, какой объем бюджета (с точки зрения использования ЦП) он готов пожертвовать библиотеке для проверки аргументов, и позволяет ли библиотека разумно использовать этот бюджет. Кроме того, пользователь также может установить глобальную функцию обработки ошибок, которая будет вызываться в случае обнаружения нарушенного контракта.
Что касается аспекта, что функция является частной, я не думаю, что это означает, что мы никогда не должны проверять ее аргументы. Мы можем больше доверять нашему собственному коду, чтобы не нарушать контракт внутренней функции, но мы также знаем, что мы тоже не идеальны. Проверка аргументов во внутренних функциях так же полезна в обнаружении наших собственных ошибок, как и в открытых функциях для обнаружения ошибок в клиентском коде.