Существует ли общепринятая идиома для обозначения кода C ++, который может генерировать исключения? - PullRequest
10 голосов
/ 11 августа 2009

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

Существуют ли установленные идиомы или "лучшие практики", которые существуют дляИмея дело с этой проблемой?

Я думал о следующем:

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

    • Достоинства: Простые.
    • Недостатки: возможны ошибки пользователя.
  2. У нас может быть приложение try/catch(...) для безопасности.

    • Плюсы: у нас больше не будет необдуманных исключений.
    • Минусы: Исключение ловится далеко от броска.Трудно понять, что делать или что пошло не так.
  3. Использование спецификаций исключений

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

Любой опыт использования этих методов или любые дополнительныеметоды, о которых я не знаю?

Ответы [ 11 ]

9 голосов
/ 11 августа 2009

Краткий ответ на вопрос о названии - идиома, обозначающая функцию, которую может выдать функция, - это , а не , чтобы задокументировать ее «эта функция не выбрасывает». То есть все может скинуть по умолчанию.

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

Если вы заботитесь только о MSVC, вы можете рассмотреть возможность использования пустой спецификации исключений или __declspec(nothrow) для функций, которые не выдают, и throw(...) для функций, которые выполняют. Это не приведет к неэффективному коду, поскольку MSVC не испускает код, чтобы проверить, что функции, объявленные nothrow, на самом деле не генерируют. GCC может сделать то же самое с -fno-enforce-eh-specs, проверьте документацию вашего компилятора. Все будет автоматически задокументировано.

Вариант 2, try-catch для всего приложения на самом деле не "для безопасности", это просто потому, что вы думаете, что вы можете сделать что-то более полезное за исключением (например, распечатать что-то и выйти чисто), чем просто позволить C ++ вызов времени выполнения terminate. Если вы пишете код в предположении, что что-то не сработает, и это действительно происходит, то вы, возможно, остались неопределенными до того, как произойдет одно из них, например, если деструктор делает ложное предположение о согласованном состоянии.

Обычно я бы сделал вариант (1): для каждого документа функции, какую гарантию исключения он предлагает - nothrow, strong, слабый или нет. Последний баг. Первый ценится, но редко, и при хорошем кодировании строго необходим только для функций подкачки и деструкторов. Да, он подвержен ошибкам пользователя, но любые средства кодирования C ++ с исключениями подвержены ошибкам пользователя. Кроме того, также выполните (2) и / или (3), если это поможет вам обеспечить соблюдение (1).

Symbian имеет предстандартный диалект C ++ с механизмом, называемым «отпуск», который в некоторых отношениях похож на исключения. Соглашение в Symbian состоит в том, что любая функция, которая может выйти, должна именоваться с L в конце: CreateL, ConnectL и т. Д. В среднем это уменьшает ошибку пользователя, потому что вы можете легче увидеть, вызываете ли вы что-то который может уйти. Как и следовало ожидать, те же люди ненавидят тех, кто ненавидит венгерскую нотацию приложений, и если почти все функции покидают ее, она перестает быть полезной. И, как и следовало ожидать, если вы напишите функцию, в имени которой не будет указано L, в отладчике может пройти много времени, прежде чем вы обнаружите проблему, потому что ваши предположения уводят вас от реальной ошибки.

7 голосов
/ 12 августа 2009

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

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

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

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

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

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

6 голосов
/ 11 августа 2009

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

2 голосов
/ 12 августа 2009

Документация, кажется, единственный разумный метод, о котором я знаю.

Что касается спецификаций исключений, вот старая (2002 г.) статья Херба Саттера на эту тему http://www.ddj.com/cpp/184401544 В ней обсуждается, почему эта особенность языка не дает нам безопасности во время компиляции, и в итоге делается вывод:

Итак, вот что кажется лучшим совет, который мы, как сообщество, узнали на сегодня:

Мораль № 1: Никогда не пишите исключение спецификация.

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

2 голосов
/ 11 августа 2009

C ++, до c ++ 11, определил спецификацию броска. См. этот вопрос . В прошлом я видел, что компиляторы Microsoft игнорируют спецификацию C ++ throw. Само понятие «проверенные исключения» является спорным, особенно в сфере Java. Многие разработчики считают это неудачным экспериментом.

2 голосов
/ 11 августа 2009

Я использую как 1, так и 2.

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

О 2: C # имеет встроенный способ отловить необработанные исключения. Так что это не «плохо», хотя я согласен, что пахнет. Иногда лучше просто аварийно завершить работу, чем работать непоследовательно, но я практиковался в том, чтобы регистрировать любые необработанные исключения и ТОГДА сбой. Это позволяет людям отправлять мне журнал, чтобы я мог проверить трассировку стека и отследить проблему. Таким образом, после каждого исправления происходит все меньше и больше сбоев.

1 голос
/ 06 февраля 2010
  1. Документирует, какой уровень исключительная безопасность функция гарантирует. Как указал Стив Джессоп в своем ответе, существует несколько уровней. В идеале, если они задокументированы для всех интерфейсов или, по крайней мере, необходимого минимума: a) никогда не выбрасывает или b) может выдать

    Читать о Гарантии безопасности исключений Абрахамса объяснил Херб Саттер .

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

  3. Не делай этого. Прочитайте Herb Sutter Прагматичный взгляд на спецификации исключений и соответствующие статьи.

1 голос
/ 12 августа 2009

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

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

0 голосов
/ 12 августа 2009

Есть способ указать, какие исключения функция может генерировать в C ++. Пример взят из книги Страуструпа:

void f(int a) throw (x2, x3);

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

0 голосов
/ 12 августа 2009

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

#include <stdexcept>

class MyException: public std::exception
{
    public:
    ~MyException() throw() {}
     char* what() const throw()
     {
        try
        {
            // Do Stuff that may throw
            return "HI";
        }
        // Exceptions must not escape this method
        // So catch everything.
        catch(...)
        {
           return "Bad Stuff HAppening Cant Make Exception Message.";
        }
     }
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...