Каталог статей
Исключения.
|
#include <eh.h>
static void se_translator(unsigned int code,_EXCEPTION_POINTERS *) { if( code == EXCEPTION_FLT_DENORMAL_OPERAND || code == EXCEPTION_FLT_DIVIDE_BY_ZERO || code == EXCEPTION_FLT_INEXACT_RESULT || code == EXCEPTION_FLT_INVALID_OPERATION || code == EXCEPTION_FLT_OVERFLOW || code == EXCEPTION_FLT_STACK_CHECK || code == EXCEPTION_FLT_UNDERFLOW) { short cw=???; __asm { fninit fldcw cw } }
throw Exception(CPUError(code)); } void Setup_SE_translator() { _set_se_translator(se_translator); }
|
Смысл
кода приблизительно в следующем. Вызовом функции _set_se_translator можно
установить функцию, которая будет получать управление в случае возникновения в
текущем потоке SEH-исключения. Главное назначение этой функции - получить код
SEH-исключения, завернуть в подходящую обёртку и выбросить нормальное C++
исключение, которое в дальше можно поймать обычным catch(). Коды этих исключений
можно получить из windows.h, а описание - в MSDN в статье про EXCEPTION_RECORD,
либо в прикрепленном файле. Среди этих кодов есть семейство особо важных,
связанных с плавающей точкой. При получении одного из этих кодов, надо делать
маленькую дополнительную обработку. А именно, нужно командой fninit сбросить
сопроцессор в нормальное состояние и загрузить подходящее слово управление.
Иначе флаги исключений по-прежнему будут висеть в сопроцессоре, что вызовет
возбуждение нового исключения при попытке его снова использовать – Вам оно
нужно? Вообще, использование исключений сопроцессора -- это отдельный
нетривиальный вопрос.
Вопрос. Я работаю с STL, очень часто использую
операцию push_back(), при этом не знаю, как контролировать ситуацию, когда
память push_back – ом не выделена, потому что push_back не возвращает код
ошибки. Как мне быть?
Всё нормально – вам необходимо ловить
исключение std::bad_alloc – именно оно генерируется в случае неудачного
проведения операции push_back. И не только push_back() – но и вообще везде, где
STL перераспределяет память – например, resize().
Вопрос. Я пытаюсь
поймать исключение std::bad_alloc при выделении памяти оператором new, но у меня
ничего не получается. Помогите! Тут возможны несколько причин. 1)
Генерация стандартного исключения std::bad_alloc возможна только стандартным
оператором new. То есть для начала необходимо сделать как минимум #include
<new>. 2) Стандарт гарантирует, что в памяти сможет расположиться
std::bad_alloc. Если вы напишете catch(std::bad_alloc){}, то при этом CRTL будет
пытаться расположить в памяти не только сам bad_alloc, но и его копию. Про копию
Стандарт C++ ничего не говорит, поэтому CRTL может игнорировать копии
bad_alloc-а. Правильнее писать: catch(std::bad_alloc &){}. 3) Вы не
загрузили std::bad_alloc в качестве new handler-а. Вот как лучше всего это
сделать.
|
//код приведён для Visual C++, в иных компиляторах возможны изменения// #include <new> #include <new.h>
//функция установки new handler-a. int _cdecl my_new_handler(size_t) { throw std::bad_alloc(); return 0; }
//это в какой нибудь функции: _PNH _old_new_handler; _old_new_handler = _set_new_handler(my_new_handler);
//тут new будет кидать исключения std::bad_alloc
_set_new_handler(_old_new_handler);
|
Обязательно
ли возвращать old_new_handler на место – не знаю, скорее всего необязательно. По
моему - лучше всего это сделать один раз в самом начале программы, а по
завершении – вернуть old_new_handler. С другой стороны производительность
стандартного оператора new (как и всего остального стандартного) немного
хромает, если вы желаете добиться экстра производительности – то old_new_handler
лучше вернуть на место. В общем – я предупредил – остальное на вашей
совести.
4) Вы работаете с MFC. В этом случае вы можете поймать только
указатель на исключение CException либо производный от него. В этом случае, если
вы будете, например, пытаться выделить большое количество памяти, то MFC будет
упорно кидать сообщение «Out of memory». И с этим ничего поделать нельзя –
придётся ловить MFC исключения (не помогает даже ручная установка new
handler-а), это видимо сделано под девизом «Мы в Майкрософт, всегда считаем, что
стандарт можно улучшить» (Copyright кто то из MS, но не Билл
Гейтс);
Вопрос. У меня что то случилось с размером контейнера при
вызове исключения std::bad_alloc – size() вернул одно, а перечисление с помощью
итератора – на один элемент больше. Такое бывает если исключение кидает
конструктор копии – size() не учитывает недоконструированный элемент, а при
перечислении он может и остаться, это касается контейнеров std::list, std::dequе
и других. Это – «особенность дизайна» некотрых реализаций STL, например, той,
что поставляется с Visual C++. Exception safety контейнеров стандартной
библиотеки была добавлена в последний момент процесса стандартизации, поэтому
далеко не все реализации контейнеров правильно ведут себя в присутствии
исключений. Так версия STL от Dinkumware, что поставляется с VC 6 тянется ещё со
времен VC 4.2, т.е. года 1994 - последняя версия стандарта C++ вышла в 1998 году
(комментировать нужно?). Бороться с этим можно путём обновления STL на более
свежую реализацию (например, от STLPort – www.stlport.com). Либо не бросать
исключения в конструкторах.
Вопрос. Перечислите плюсы и минусы
использования SEH по сравнению с обычными CRTL исключениями. Плюсы: 1)
позволяет ловить больший спектр исключений, к которым относится деление на 0,
переполнение стека, и т.д. 2) обработка исключений ведётся на уровне ядра
операционной системы (в WinNT образных ОС); 3) возможность использовать
исключения без CRTL. Часто для уменьшения размера программы её собирают без
CTRL. В этом случае использовать «стандартные» C++ исключения невозможно. SEH
можно будет воспользоваться, если загрузить kernel32.dll.
Минусы: 1)
плохая совместимость с С++. SEH исключения реализованы на уровне ядра ОС,
которое ничего не знает про С++, например про классы. Если произошла
исключительная ситуация, то SEH не гарантирует, что уберёт за собой весь мусор,
потому что не будут вызваны деструкторы пользовательских классов. Это связано с
тем, что если компилятор не видит генерации C++-исключений, то он и не создает
код, который отвечает за размотку стека при исключениях (только при
использовании слов __try, __except и т.д.). 2) невозможность (в пределах
одной функции) пользоваться SEH и стандартными исключениями
одновременно.
Вопрос. Как использовать SEH исключения?
|
__try { //.... } __except(GetExceptionCode() == ….) //подставить нужное слово { //.... }
или
__except(EXCEPTION_EXECUTE_HANDLER) //подставить нужное слово { //... }
__try { //.... } __finaly { //... }
|
GetExceptionCode()
– возвращает код возникшего исключения – можно использовать для вывода
диагностического сообщения. Если Вы используете слово __finaly, то этот блок
будет выполнен в любом случае, даже если попытаться выйти из блока __try с
помощью return; В одном блоке __except и __finaly одновременно быть не
могут.
Кроме того, можно получить машинно-независимую информацию об
исключении, при помощи функции GetExceptionInformation().
Структурная
обработка особых ситуаций средствами Win32 API
Вопрос. Как ловить MFC
исключения?
Я приведу пример, как можно ловить исключения при работе
с файлами, а за подробностями отправлю к MSDN.
|
CFile f; CFileException *pE = new CFileException; TCHAR szErrorString[255];
if (f.Open(m_sDraftName, CFile::modeRead | CFile::shareDenyWrite, pE) == FALSE) { pE->ReportError(MB_OK | MB_ICONSTOP); pE->GetErrorMessage(szErrorString, 255); WriteErrorInLogFile(szErrorString); //функция записи в лог (пользовательская). pE->Delete(); return FALSE; } delete pE;
|
У
класса CException и его производных имеется метод ReportError – который выводит
на экран сообщение об ошибке. Так же из этого сообщения можно просто
сформировать строку, например, для вывода в log файл. Для этого есть метод
GetErrorMessage(); Так же MFC исключения можно ловить дедовским способом
try/catch.
Вопрос. В Visual C++ я видел операторы try и TRY. В чём
отличие и чем лучше пользоваться?
Макросы
TRY/CATCH/AND_CATCH/END_CATCH/THROW/THROW_LAST тянутся из тех времен, когда
компилятор C++ от MS еще не поддерживал стандартную обработку исключений.
Пользоваться ли ими – это уже ваш выбор, но в свете сказанного ранее – не
советую.
Вопрос. Как насчёт быстродействия кода получаемого при
использовании исключений? Быстродействие его практически не страдает, но
вот объём существенно возрастает. И всё из за добавления кода «для
отката».
Вопрос. Как насчёт исключений в UNIX-like
системах? В UNIX-ах при возникновении исключений система шлёт сигналы,
например, при возникновении ошибки с плавающей точкой FreeBSD шлёт сигнал SIGFPE
– Floating Point Exception.
Вопрос. Нестандартное использование
исключений. Естественно, можно использовать исключения в нестандартных
ситуациях. Например, для выхода из многоступенчатого цикла – т.е. там, где break
не сработает.
|
try { for(..) { for(..) { if(...) throw;//генерация исключения } } } catch(..) { }
|
Более
подробную информацию по ловле исключений читайте в прикрепленном файле. (40 927
байт в zip архиве, всё написано русским по белому – кто не испугался – срочно
качаем!).
Вот ещё одна полезная ссылка по теме: http://msdn.microsoft.com/library/default....xceptdotnet.asp
Осталось
неосвещенным много чего. Перечислю:
1) Exception handling в DOS, Win9x,
UNIX. 2) Exception handling в компиляторах Borland (я слышал, что им не нужно
изгаляться с преобразование C++ исключения в SEH, а что Borland кидает
исключительно SEH исключения? – Borland не претендует на универсальность, и в
данном случае это просто прекрасно!). 3) Exception handling в ИмяРек
компиляторах. 4) Exception handling в OLE/COM. 5) Ещё я слышал, что
появилась VEH обработка исключений. Пользовались? Я нет. Поделитесь опытом
|
| Категория: С/С++/MFC | Добавил: Cromartie (31.01.2013)
|
| Просмотров: 506
| Рейтинг: 0.0/0 |
Добавлять комментарии могут только зарегистрированные пользователи. [ Регистрация | Вход ]
|
| Статистика |
Онлайн всего: 1 Гостей: 1 Пользователей: 0 |
|
|