Существует множество мнений по поводу механизма исключений: некоторые называют исключения "скрытым goto", другие же считают исключения отличным механизмом и предлагают его использовать везде и всегда. Единственное, с чем согласны все - исключения нужно использовать с особой осторожностью. Ведь даже с виду совсем безобидный код может привести к тому, что ваше приложение упадёт в самый неподходящий момент.
С++, как многим известно, предоставляет некий механизм спецификации исключений - exceptions spectifications(п.15.4), простейший пример которого представлен ниже:
void foo() throw(Exception1, Exception2)В этом небольшом примере функцию foo описывают как функцию, которая может бросить исключения Exception1 и Exception2. Однако, как пишут в Boost Requirements and Guidelines:
{
//...
}
The biggest problem with exception-specifications is that programmers use them as though they have the effect the programmer would like, instead of the effect they actually have.или
Основная проблема со спецификацией исключений в том, что программисты используют их, как будто они работают так, как этого хочет программист, а не так, как они действуют на самом делеДействительно, попробуйте в функции foo бросить исключение Exception3 и скомпилировать при помощи GCC(другие компиляторы я не использую, но думаю, что они действуют также). Ничего не произошло? И у меня то же самое :)
Т.е., спецификация исключений - вещь относительно бесполезная, а иногда и вредная(некоторые компиляторы не инлайнят функции с exception specifications). Существует много тредов-обсуждений в листе рассылки GCC по поводу возможности вывода предупреждений о несоответствующем спецификации использовании исключений(вот это сообщение - самое полезное, по-моему). В конце концов, подобные запросы отклоняются, ибо "невозможно реализовать из-за сложности межпроцедурного анализа. GCC не предназначен для такого".
Однако, это не остановило проект EDoc++, о котором и хотелось бы рассказать подробней. Он использует пропатченный GCC для анализа кода на ошибки при использовании исключений. Например, у нас есть код:
#include <iostream>
class C1{};
class C2{};
void foo() throw (C1)
{
throw C2();
};
class C
{
public:
~C()
{
foo();
}
};
int main()
{
C c;
}
В этом примере есть несколько ошибок, связанных с исключениями:
- Не соблюдается спецификация в функции foo
- Бросается исключение из деструктора
- Исключение выходит за функцию main, что приводит к аварийному завершению программы
EDOC -> g++ -fedoc-source -c ~/dev/sandbox/t.cpp -o ~/dev/sandbox/t.oEDoc++ создает дополнительный файлик, который при опции -fedoc-source попадает в ~/dev/sandbox/t.cpp.edc. Показываем полученные результаты:
EDOC -> edoc --show-all --format simple ~/dev/sandbox/t.cpp.edcКажется, лучше сообщение об ошибке и не укажешь. Исправим ошибку - будем делать throw C1; вместо throw C2;. Перекомпилируем и посмотрим:
F: _GLOBAL__D__Z3foov()
F: _GLOBAL__I__Z3foov()
F: foo()
F: __static_initialization_and_destruction_0(int, int)
F: C::~C()
F: main()
ERROR(ECRASH_SPEC): Exception may propogate through restrictive specifier :The function: foo() can throw an exception of type: C2 that is not allowed by its specifier list: throws(C1)
EDOC -> edoc --show-all --format simple ~/dev/sandbox/t.cpp.edcАга! Мы чуть-чуть не упустили исключение за пределы деструктора. Это ужасно. Уберём наконец throw С1; из foo для того чтобы проверить, как EDoc++ отреагирует на спецификацию без реального выбрасывания исключений:
[...skip...]
ERROR(EMAIN_EXC): An exception may propogate out the main function. :The exception: C1 may propagate out the main function which may cause a program abort.
WARNING(WDESTR_EXC): An exception may propogate through a destructor. :Destructor: C::~C(), Exception: C1
EDOC -> edoc --show-all --format simple ~/dev/sandbox/t.cpp.edcМолчит. Т.е., он не принимает во внимание спецификации, если эти спецификации врут, что, в принципе, правильно.
[...skip...]
EDoc++ обладает еще множеством достоинств: он поддерживает множество форматов вывода, множество способов хранения .edc информации, судя по отличной документации он правильно работает с указателями на функции, которые бросают исключения, и умеет находить ошибки даже в тех случаях, когда они делятся между несколькими модулями компиляции. В общем, отличный инструмент, спасибо создателям :)
он не принимает во внимание спецификации, если эти спецификации врут, что, в принципе, правильно.
ReplyDeleteможет я и ошибаюсь, но по моему спецификация исключений указывает на то какие исключение функция может бросать, а не будет бросать :). Ага вот из текущего драфта - 15.4.1 - A function declaration lists exceptions that its function might directly or indirectly throw by using an exception-specification as a suffix of its declarator.
И отдельное спасибо за ссылку на тул :)
@Yuriy Volkov:
ReplyDeleteВ данном случае функция ну никак не может бросить исключение, описанное в спецификаии, потому я и написал, что спецификация врёт. Положительная сторона EDoc++ в том, что создатели правильно понимают стандарт и не полагаются на спецификации при поиске ошибок. Отрицательная сторона - хотелось бы получить хинт по тому, что в спецификации указан лишний exception. Вероятно, это настраиваемо