Подтвердить что ты не робот

С++ - аргументы для исключений по возвратным кодам

У меня есть дискуссия о том, каким путем перейти в новый проект на С++. Я предпочитаю исключения по кодам возврата (только для исключительных случаев) по следующим причинам -

  • Конструкторы не могут предоставить код возврата
  • Отменяет путь отказа (который должен быть очень редок) от логического кода, который является более чистым
  • Быстрее в не исключительном случае (нет проверки, если/еще сотни тысяч раз)
  • Если кто-то завинчивает настройки кода возврата (забывает вернуть FAIL), для отслеживания может потребоваться очень много времени.
  • Лучшая информация из сообщения, содержащегося в ошибке. (Мне было указано, что возвращаемый список может сделать то же самое для кодов ошибок)
  • От Jared Par Невозможно игнорировать без кода, специально разработанного для его обработки.

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

4b9b3361

Ответ 1

Я думаю, что это статья подводит итог.

Аргументы для использования исключений

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

Аргументы против использования исключений

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

Ответ 2

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

  • Написание исключающего кода кода сложно [на С++].

С большим количеством недавнего опыта в С# я могу сопереживать с вашим желанием использовать исключения, но, к сожалению, С++ не является С#, и многие вещи, которые мы можем избежать на С#, могут быть в конечном счете смертельными в С++.

Хорошее суммирование case for и против можно найти в Руководства по стилю Google. Короче говоря:

Плюсы:

  • Исключения позволяют более высоким уровням приложения решать, как "не могут произойти" с ошибками в глубоко вложенные функции, без скрытая и подверженная ошибкам бухгалтерская отчетность кодов ошибок.
  • Исключения используются большинством других современных языков. Используя их в С++ сделает его более согласованным с Python, Java и С++, которые другие знакомы с.
  • Некоторые сторонние библиотеки С++ используют исключения и отключают их внутренне затрудняет интегрироваться с этими библиотеками.
  • Исключения являются единственным способом отказа конструктора. Мы можем имитировать это с помощью функции factory или Init(), но для этого требуется куча распределения или нового "недействительного" состояния, соответственно.
  • Исключения действительно удобны в тестировании фреймворков.

Минусы:

  • Когда вы добавляете оператор throw к существующей функции, вы должны изучить все его транзитивные абоненты. Либо они должны сделать хотя бы основные гарантии безопасности исключений или они никогда не должны улавливать исключение и быть довольным программой заканчивая в результате. Например, если f() вызывает g() вызовы h(), а h выдает исключение, которое f ловит, g должен быть осторожен или не может очищать правильно.
  • В более общем плане исключения делают контроль потока программ сложным оценить, просмотрев код: функции могут возвращаться в местах, где вы не ожидайте. Это приводит к ремонтопригодность и отладка сложности. Вы можете минимизировать это стоимость через некоторые правила о том, как и где исключения могут быть использованы, но стоимость более того, что разработчик должен знать и понимать.
  • Безопасность исключений требует как RAII, так и разных методов кодирования. Требуется много вспомогательного оборудования для написания правильных исключений код легко. Кроме того, чтобы избежать читателей, чтобы понять весь звонок граф, исключающий код, должен изолировать логику, которая записывает постоянное состояние в "фиксацию" фаза. Это будет иметь как преимущества и расходы (возможно, когда вы вынуждены для обфускации кода, чтобы изолировать фиксации). Разрешение исключений заставлять нас всегда оплачивать эти расходы даже если они не стоят того.
  • Включение исключений добавляет данные в каждый созданный бинар, увеличивая время компиляции (возможно, немного) и возможно увеличение адресного пространства давление.
  • Доступность исключений может побуждать разработчиков бросать их когда они не подходят или оправиться от них, когда это не безопасно для этого. Например, неверный пользователь входные данные не должны вызывать исключения из быть брошенным. Нам нужно будет стиль руководства еще дольше документа эти ограничения!

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

Ответ 3

ИМХО, причина №1 для исключения исключений по кодам возврата - вы не можете молча игнорировать исключение. Для этого требуется как минимум минимальный объем дополнительного кода.

Ответ 4

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

Во-первых, стандартная библиотека С++ использует исключения самостоятельно, повсюду. Вы не можете использовать классы контейнеров или iostreams без их присутствия. Поскольку многие полезные функции будут использовать их, попытка обойтись без них будет представлять множество проблем.

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

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

В-четвертых, действительно сложно сделать старый код исключительным. Однако это новый проект.

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

Ответ 5

Быстрее в не исключительном случае (нет проверки, если/еще сотни тысяч раз)

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

Если кто-то завинчивает настройки кода возврата (забывает вернуть FAIL), для отслеживания может потребоваться очень много времени.

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

Ответ 6

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

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

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

Ответ 7

Как правило. когда восстановление возможно и ожидается, используйте коды возврата.

Если восстановление невозможно или не требуется, используйте исключения.

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

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

Сделайте это, используя исключения, если это необходимо.

Ответ 8

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

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

long Foo( )
{
    long retCode = MoveStage( );
    if ( retCode != RetCode_OK )
    {
        logger->LogError( __LINE__, __FILE__, "some message" );
        return( retCode );
    }

    int someConfigVar = 0;
    long retCode = GetConfigVar( "SomeVarName", someConfigVar );
    if ( retCode != RetCode_OK )
    {
        logger->LogError( __LINE__, __FILE__, "some message" );
        return( retCode );
    }

    long retCode = DoSomething( );
    if ( retCode != RetCode_OK )
    {
        logger->LogError( __LINE__, __FILE__, "some message" );
        return( retCode );
    }

    // and on and on and on...
}

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

Ответ 9

Очень сложно написать безопасный код исключения. Совершенно надуманный пример: -

void Class::Method()
{ 
  i++;
  SomeCallThatMightThrow();
  j++;
}

Замените я ++ и j ++ любыми двумя переменными, ресурсами, состояниями, которые должны оставаться синхронными. Сбор мусора спас нас от необходимости помнить о том, чтобы спарить наши новые и удалять. По иронии судьбы, старомодное явное тестирование кода возврата спасает нас от необходимости тщательно анализировать каждую функцию, которая может генерировать исключения, чтобы проверить, что они не были привинчены с пост-условиями.

Ответ 10

Одна из вещей, которые мне нравятся в С++, заключается в том, что очень легко понять, как функции более высокого уровня могут быть реализованы с точки зрения возможностей C (которые легко понять с точки зрения сборки). Исключения для С++ нарушают эту форму. Чтобы понять этот уровень понимания, я должен многое сделать. Просто прочитайте этот, и вы потратите много времени, почесывая голову, прежде чем понимать это.

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

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

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

Я не понимаю, как это выгодно.

Ответ 11

  • Конструкторы не могут дать код возврата (за исключением того, что вы, возможно, бросаете исключения в конструкторе, в котором вы находитесь в мире обид)

  • Отменяет путь отказа (который должен быть очень редок) от логического кода, который является более чистым (Открывает гораздо более широкий путь отказа. Исключения С++ совсем не похожи на С#. Я люблю исключения С#. Исключения в С++ хуже, чем бесполезно. Частично из-за реализации, отчасти из-за того, что С++ есть и нет)

  • Быстрее в не исключительном случае (нет проверки, если/еще сотни тысяч раз) (Не совсем. Вы должны проверять одинаковое количество ошибок, несмотря ни на что, вы просто не видите, где они проверяются. Кроме того, есть ошибки, которые имеют значение, и те, которые не имеют или, по крайней мере, имеют значение для вашего кода, поэтому вам не нужно проверять каждую вещь (вы проверяете, не вызвала ли новая ошибка?))
  • Если кто-то завинчивает настройки кода возврата (забывает вернуть FAIL), для отслеживания может потребоваться очень много времени. (Я возьму миллион таких ошибок над любой из распространенных ошибок, связанных с исключениями С++).
  • Лучшая информация из сообщения, содержащегося в ошибке. (Мне было указано, что возвращаемый список может сделать то же самое для кодов ошибок) (Ну, как вы реализуете свою обработку ошибок? Это зависит от вас, не так ли? У вас могут быть исключения, которые не дают никакой полезной информации или кодов ошибок, которые выводят лишние данные об ошибке. В С++ они, похоже, никогда не дают полезной информации в тех случаях, когда вы действительно хотите их).
  • От Jared Par Невозможно игнорировать без кода, специально разработанного для его обработки (Конечно, но этот код, как правило, бесполезный код. Это как принятие закона, в котором говорится, что каждый должен иметь бесплатные программы. Люди, которые в любом случае не подчиняются таким законам, а также люди, которые уже справляются с ошибками, все равно)

Как по причинам против:

  • Желая, чтобы ваша программа действительно работала хотя бы некоторое время.
  • производительность.
  • ИДЕОЛОГИЯ. Если это ошибка, мы не должны "восстанавливать" ее. Мы либо умираем с сообщением об ошибке, и регистрируемся, и продолжаем, или полностью игнорируем его. Если бы мы могли это исправить, это была бы не ошибка, а функция... и если вы используете такой код, как преднамеренно, инструменты отладки в мире не помогут вам.

В основном один.