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

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

Резюме

Не бросайте камни в соседский огород — поскольку нет повсеместно распространенного бинарного стандарта для обработки исключений С++, не позволяйте исключениям пересекать распространяться между двумя частями кода, если только вы не в состоянии контролировать, каким компилятором и с какими опциями скомпилированы обе эти части кода. В противном случае может оказаться, что модули не поддерживают совместимые реализации распространения исключений. Обычно это правило сводится к следующему: не позволяйте исключениям пересекать границы модулей/подсистем.

Обсуждение

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

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

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

• Вокруг функций обратного вызова из кода, который находится вне вашего контроля. Операционные системы и библиотеки часто используют схему, при которой вы передаете указатель на функцию, которая будет вызвана позже (например, при некотором асинхронном событии). Не позволяйте исключениям распространиться за пределы вашей функции обратного вызова, поскольку вполне возможно, что код, вызывающий вашу функцию, использует иной механизм обработки исключений. Кстати говоря, он может вообще быть написан не на С++.

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

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

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

Убедитесь, что каждый модуль согласованно использует одну и ту же внутреннюю стратегию обработки ошибок (предпочтительно — исключения С++; см. рекомендацию 72) и единую стратегию обработки ошибок в интерфейсе (например, коды ошибок для API на языке С); обе эти стратегии могут быть одинаковы, но обычно это не так. Стратегии обработки ошибок могут изменяться только на границах модуля. Определите, как происходит связывание стратегий между модулями (например, как происходит взаимодействие с COM или CORBA, или что всегда следует перехватывать исключения на границе с API на языке С). Хорошим решением будет определить центральные функции, которые выполняют преобразования между исключениями и кодами ошибок, возвращаемых подсистемой. Так вы сможете легко транслировать входящие ошибки от других модулей в используемые внутренние исключения и тем самым упростить интеграцию.

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

Небольшое предостережение. Некоторые операционной системы используют механизм исключений С++ при обработке низкоуровневых системных ошибок, как, например, разыменование нулевого указателя. Следовательно, инструкция catch(...) может перехватить больше исключений, чем вы ожидаете, так что ваша программа может оказаться в неопределенной ситуации при выполнении перехвата catch(...). Обратитесь к документации по вашей системе и либо приготовьтесь к обработке таких низкоуровневых исключений наиболее разумным способом, который сможете придумать, либо используйте в начале вашего приложения соответствующие системные вызовы для отключения такого поведения. Замена catch(...) последовательностью перехватов catch(E1&){/*...*/} catch(E2&){/*.. .*/} ... catch(En&){/*...*/} для всех известных типов базовых исключений Ei масштабируемым решением не является, поскольку вам придется обновлять этот список при добавлении новых библиотек (использующих собственные иерархии исключении) в ваше приложение.

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

Ссылки

[Stroustrup00] §3.7.2, §14.7 • [Sutter00] §8-17 • [Sutter02] §17-23 • [Sutter04] §11-13

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

Границы и заливка

Из книги Компьютер на 100. Начинаем с Windows Vista автора Зозуля Юрий

Границы и заливка Для применения к выделенному фрагменту границ или заливки можно использовать следующие кнопки в группе Абзац вкладки Главная. Заливка. Позволяет выполнить заливку выбранным фоном. Щелчок на кнопке сразу же применяет текущий цвет заливки для текущего


Ода ошибкам и исключениям

Из книги Язык программирования С# 2005 и платформа .NET 2.0. [3-е издание] автора Троелсен Эндрю

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


Границы и заливка

Из книги Excel. Мультимедийный курс автора Мединов Олег

Границы и заливка По умолчанию ячейки не имеют видимых границ: вы видите тонкие серые линии, разграничивающие ячейки, но эти линии не отображаются при печати документа. Иногда же требуется, чтобы напечатанная таблица имела четкие границы (рис. 3.10). Рис. 3.10. Пример


Границы

Из книги Искусство программирования для Unix автора Реймонд Эрик Стивен

Границы Рассмотрим, как можно создать видимые границы ячейки, которые будут отображаться при печати.Прежде всего напомню, что перед какими-либо преобразованиями ячейки (или диапазон) должны быть выделены.Обратите внимание на кнопку Границы, расположенную в группе Шрифт


3.1.4. Внутренние границы

Из книги Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ автора Мейерс Скотт

3.1.4. Внутренние границы В Unix действует предположение о том, что программист знает лучше (чем система). Система не остановит пользователя и не потребует какого-либо подтверждения при выполнении опасных действий с данными, таких как ввод команды rm -rf *. С другой стороны, Unix


Правило 8: Не позволяйте исключениям покидать деструкторы

Из книги Искусство программирования для Unix автора Реймонд Эрик Стивен

Правило 8: Не позволяйте исключениям покидать деструкторы C++ не запрещает использовать исключения в деструкторах, но это, безусловно, очень нежелательная практика. На то есть серьезная причина. Рассмотрим пример:class Widget {public:...~Widget() {...} // предположим, здесь есть


3.1.4. Внутренние границы

Из книги Справочник по CSS автора Коллектив авторов

3.1.4. Внутренние границы В Unix действует предположение о том, что программист знает лучше (чем система). Система не остановит пользователя и не потребует какого-либо подтверждения при выполнении опасных действий с данными, таких как ввод команды rm -rf *. С другой стороны, Unix


Границы элементов

Из книги Антимозг [Цифровые технологии и мозг] автора Шпитцер Манфред

Границы элементов borderЗадает все свойства границ элемента страницы в один прием.Заменяет атрибуты border-color, border-style и border-width. Значения этих атрибутов могут располагаться в любом порядке.border: [{border-color}] [{border-style}] [{border-width}];Значение по умолчанию medium none. Поддерживается IE начиная


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

Из книги Компьютер – это просто! автора Алиев Валерий

Не позволяйте рыночным крикунам сбивать вас с толку Мы видели, что в отношении средств массовой информации и коммуникации распространяется много неправды и используется тактика напускания тумана. Что можно предпринять против этого? Не позволяйте сбивать себя с толку!


Таблицы и границы

Из книги HTML, XHTML и CSS на 100% автора Квинт Игорь

Таблицы и границы ТаблицаВсе мы интуитивно понимаем, о чем идет речь, когда произносится слово «таблица», но будет лучше, если мы все-таки договоримся о терминах и будем их употреблять в одинаковом смысле.Под таблицей понимается набор строк и столбцов, пересечения


5.2. Границы фрейма

Из книги автора

5.2. Границы фрейма Обращаю ваше внимание на то, что в описанном выше примере каждый фрейм имеет границу (см. рис. 5.1). Границей фрейма можно управлять, как и любыми другими элементами фрейма. Для этого существует несколько атрибутов элемента FRAMESET, позволяющих настраивать


Границы

Из книги автора

Границы Для задания границ вы можете использовать следующие свойства: border, border-collapse и border-spacing.Свойство border задает стиль границы и размер. Стили границ будут подробно рассмотрены в следующих разделах. Размер границы задается целым числом и с указанием единицы


9.7. Границы

Из книги автора

9.7. Границы Для всех элементов страницы вы можете задать границы, а также установить их ширину, цвет и стиль. Начнем изучение границ со свойства


Ширина границы

Из книги автора

Ширина границы Рассмотрим свойства border-top-width, border-right-width, border-bottom-width, border-left-width, которые определяют значение ширины верхней, правой, нижней и левой границы элемента страницы соответственно. Все свойства, рассматриваемые в данном разделе, могут принимать одно из следующих


Цвет границы

Из книги автора

Цвет границы Как и для ширины, для задания цвета существует четыре свойства для четырех сторон: border-top-color, border-right-color, border-bottom-color, border-left-color и сокращенная запись – border-color. Они задают цвет верхней, правой, левой и нижней границ соответственно. Для всех этих свойств вы можете