Как throw, try {} catch {} следует использовать в реальном мире? - PullRequest
6 голосов
/ 01 октября 2011

Я имею в виду, что я знал все языковые правила для throw, попробуй {} catch {}, но я не уверен, правильно ли я их использую в реальном мире. Пожалуйста, смотрите следующий пример:

У нас есть большой кусок научного кода, который выполнял все виды обработки изображений, недавно мы решили усовершенствовать его и сделать его более надежным. Одной из часто используемых подпрограмм является void rotate_in_place (float * image, image_size sz) ;

Чтобы сделать его более надежным, мы добавим некоторую проверку работоспособности в начале кода:

void rotate_in_place(float* image, image_size sz) {
    // rotate_in_place does not support non-square image;
    if (sz.nx != sz.ny)  throw NonSquareImageError;
    // rotate_in_place does not support image too small or too large
    if (sz.nx <= 2 || sz.nx > 1024)  throw WrongImageSizeError;
    // Real rode here
    .....
}

Теперь проблема в том, что rotate_in_place () используется более чем в 1000 местах. Должен ли я обернуть каждый вызов rotate_in_place () функцией try {} catch {}, мне кажется, код будет невероятно раздутым. Другая возможность - не оборачивать try {} catch {} и не запускать программу, но чем это отличается от простого использования

if (sz.nx != sz.ny) {
    cerr << "Error: non-squared image error!\n";
    exit(0);
}

Короче говоря, я не уверен в реальной пользе использования throw, try, catch, каких-либо хороших предложений?

Ответы [ 8 ]

6 голосов
/ 01 октября 2011

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

Печать ошибки и использование exit плохо по трем причинам:

  1. Вы не можете обработать ошибку.exit не обрабатывается (если только это не происходит, когда ошибка абсолютно критична, но ваша функция не может этого знать - у вызывающей стороны может быть способ ее восстановления).
  2. Вы расширяете обязанности функции с помощью записи вжестко закодированный поток, который может быть даже недоступен (это rotate_in_place, а не rotate_in_place_and_print_errors_and_kill_the_program_if_something_is_wrong) - это ухудшает возможность повторного использования.
  3. При таком подходе вы теряете всю информацию отладки (вы можете генерировать трассировки стека изнеобработанные исключения, вы ничего не можете сделать с функцией, которая выдает ошибку каждый раз - необработанное исключение - это ошибка, но это ошибка, которой вы можете следовать до источника).
5 голосов
/ 01 октября 2011

Общее правило для исключений: "Заинтересован ли сайт немедленного вызова о том, что здесь происходит?" Если сайт вызова заботится, то возвращение кода состояния, вероятно, имеет смысл. В противном случае, метание имеет больше смысла.

Рассмотрим это следующим образом - конечно, у вашего метода rotate in place есть несколько недопустимых типов аргументов, и в этом случае вы, вероятно, должны выбросить std::invalid_argument. Маловероятно, что вызывающий абонент rotate_in_place хочет иметь дело или знает, как поступить, например, с изображением, которое не было квадратным, и поэтому это, вероятно, лучше выразить как исключение.

Еще одна возможность - не пытаться завернуть {} catch {} и позволить выход из программы, но чем это отличается от использования

if (sz.nx != sz.ny) {
    cerr << "Error: non-squared image error!\n";
    exit(0);
}

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

Это также имеет преимущества для вас прямо сейчас, а именно: вам не нужно вводить <iostream> в эту единицу перевода просто для того, чтобы писать ошибки.

Я обычно использую шаблон примерно так:

int realEntryPoint()
{
    //Program goes here
}

int main()
{
    //Allow the debugger to get the exception if this is a debug binary
    #ifdef NDEBUG
    try
    #endif
    {
      return realEntryPoint();
    }
    #ifdef NDEBUG
    catch (std::exception& ex)
    {
      std::cerr << "An exception was thrown: " << ex.what() << std::endl;
    }
    #endif
}
4 голосов
/ 01 октября 2011

Теперь проблема в том, что rotate_in_place () используется более чем в 1000 местах. Должен ли я обернуть каждый вызов rotate_in_place() с помощью try{} catch {}, мне кажется, это сделает код невероятно раздутым.

Это будет, и это лучше, чем цель использования исключений в первую очередь.

Другая возможность - не оборачивать какие-либо попытки {} catch {} и позволить программе выйти, но какэто отличается от простого использования [...]

То, что вы всегда можете изменить место обработки исключений позже.Если в какой-то момент вы найдете более подходящее место для разумной обработки ошибки (возможно, восстановления после нее), тогда вам нужно указать catch.Иногда это та самая функция, где вы бросаете исключение;иногда это происходит в цепочке вызовов.

На всякий случай вставьте catch -все в main.Получение исключений из стандартных, таких как std::runtime_error, делает это намного проще.

3 голосов
/ 01 октября 2011

Смысл в использовании обработки исключений заключается в следующих простых правилах:

  • Как только может произойти что-то плохое из-за неправильного ввода данных пользователем (внутренняя логика должна обрабатываться посредством утверждений / ведения журнала), выведите исключение. Бросьте как можно скорее и как можно больше: исключения C ++ обычно довольно дешевы по сравнению, скажем, с .Net.
  • Позвольте распространению исключения, если вы не можете обработать ошибку. Это значит почти всегда.

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

1 голос
/ 01 октября 2011

Что вы можете сделать, это заставить rotate_in_place возвращать логическое значение, если вызов функции был успешным. И вернуть повернутое изображение через параметр функции.

bool rotate_in_place(float* image, image_size sz, float** rotated_image) {
    // rotate_in_place does not support non-square image;
    if (sz.nx != sz.ny)  return false;
    // rotate_in_place does not support image too small or too large
    if (sz.nx <= 2 || sz.nx > 1024)  return false;
    // Real rode here
    .....
    return true;
}
1 голос
/ 01 октября 2011

Использование исключений позволяет вызывающей стороне решать, как обрабатывать ошибку.Если вы вызываете exit непосредственно внутри функции, то программа завершит работу, и вызывающая сторона не сможет решить, как обработать ошибку.Кроме того, при exit объекты стека не будут разматываться.: - (

0 голосов
/ 01 октября 2011

Хорошо, я согласен, что действительно использование Исключений приводит к раздутому коду. Это главная причина, по которой мне они не нравятся.

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

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

0 голосов
/ 01 октября 2011

Это зависит.

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

...