Правильная обработка исключения

Насколько я понимаю, в современном C++ предпочтительнее использовать throw вместо возврата кода ошибки. То есть, если у меня есть какая-то функция:

void MyFunction(int foo, double bar){
    // Do some stuff....

    if (exception_criteria_met){
        throw "Call to MyFunction with inputs" << foo << " and " << bar << " failed because ...";
    }
};

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

void MyOuterFunction(int foo){
    // Do some stuff...

    try {
        MyFunction(foo, bar);
    } catch (const char * exception_message) {
        throw "Call to MyOuterFunction with inputs " << foo << " failed because call to MyFunction with ....";
    };

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

(В качестве второстепенного вопроса, если бы MyFunction(foo,bar) вернул значение, которое я хотел использовать, не означало бы это, что после выхода из блока try { } возвращаемое значение выпало бы из области видимости? Означает ли это, что я должен избегать использования возвращаемых функцией значений? если я буду использовать try-catch?)

My understanding is that in modern C++, it is preferable to use throw instead of returning an error code. Не совсем так. Похоже, мы возвращаемся к возвращению ошибок, а не их выдаче, см., например, std::expected
tkausl 14.02.2023 21:23

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

463035818_is_not_a_number 14.02.2023 21:25

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

Mehran 14.02.2023 21:32

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

john 14.02.2023 21:55

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

nielsen 14.02.2023 22:13

Если вы не используете C++23 для std::expected, есть результат Boost и результат.

Eljay 14.02.2023 22:20
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
133
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

то во всех функциях, которые вызывают MyFunction, я должен реализовать что-то вроде:

Это не правильно.

Функция, вызывающая MyFunction, которая потенциально выдает бросок, может выглядеть так:

void MyOuterFunction(int foo){
    // Do some stuff...
    MyFunction(foo, bar);
};

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

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

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

С другой стороны, поймать и бросить как нечто другое может быть очень полезно при пересечении границы библиотеки.

user4581301 14.02.2023 21:48

Другие вопросы по теме