
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[216.73.216.30] |
![]() |
|
Сообщ.
#1
,
|
|
|
Возникли некоторые непонятки. Вот прочитал главу Рихтера об исключениях. Он пишет, что исключения С++ раскрываются компилятором в SEH-исключения с дополнительной проверкой типа аргумента в блоке catch. Логично предположить что условие catch(...) будет отличаться от __except только тем, что не будет возможности продолжать выполнение программы (значение фильтра исключения EXCEPTION_CONTINUE_EXECUTION).
Есть ключ компилятора /EH (Exception Handling Model): a - The exception-handling model that catches asynchronous (structured) and synchronous (C++) exceptions. s - The exception-handling model that catches C++ exceptions only and tells the compiler to assume that extern C functions do throw an exception. c - If used with s (/EHsc), catches C++ exceptions only and tells the compiler to assume that extern C functions never throw a C++ exception. /EHca is equivalent to /EHa. Если ключ компилятора /EH не задан, то по умолчанию: If /EH is not specified, the compiler will catch structured and C++ exceptions, but will not destroy C++ objects that will go out of scope as a result of the exception. Собственно вопросы: 1) В чём различие между С-программой и C++-программой в плане обработки исключений? Как вообще отличить С-программу от С++? Только по списку функций и соответственно по типу используемых библиотек или ещё как-то? 2) Фраза "but will not destroy C++ objects that will go out of scope as a result of the exception" означает что при возникновении С++ исключения по умолчанию не будут вызваны деструткоры для всех локальных объектов? 3) В чём отличие ключа /EHa от отсутствия ключа /EH в принципе? Может в том что будут вызываться деструкторы для С++ исключений? |
Сообщ.
#2
,
|
|
|
Цитата 1) В чём различие между С-программой и C++-программой в плане обработки исключений? в Си как мне помнится нету обработки исключений, только в С++ Цитата Как вообще отличить С-программу от С++? По использованию кода С++ Цитата 2) Фраза "but will not destroy C++ objects that will go out of scope as a result of the exception" означает что при возникновении С++ исключения по умолчанию не будут вызваны деструткоры для всех локальных объектов? да Тока в итоге не понятно что тебя интересует С++ исключения или SEH |
Сообщ.
#3
,
|
|
|
Цитата Dem_max @ в Си как мне помнится нету обработки исключений, только в С++ Ну __try/__except,__try/__finally(т.е. SEH исключения) по-любому можно использовать на с-программе. И это всё же будет ещё С-программа. То есть вы хотите сказать если я использую операторы try, catch, то это уже автоматически C++-программа. Да? Вообще, меня интересует как наилучшим образом обрабатывать исключения в программе, которая долждна работать 24х7. Микрософты рекомендуют использовать Try-Catch в С++ программе: Цитата MSDN The Win32 structured exception-handling mechanism works with both C and C++ source files. However, it is not specifically designed for C++. You can ensure that your code is more portable by using C++ exception handling. Also, C++ exception handling is more flexible, in that it can handle exceptions of any type. For C++ programs, it is recommended that you use the C++ exception-handling mechanism (try, catch, throw) described in this topic. Получается лучше использовать конструкции try/catch с опцией /EHa. Но единственный вопрос: будут ли вызваны деструкторы при С++-исключении? |
Сообщ.
#4
,
|
|
|
кстати нашел на мелкософтах
Цитата Microsoft C++ now supports the C++ exception handling model, based on the ANSI C++ Standard. This mechanism automatically handles destruction of local objects during stack unwind. If you are writing fault-tolerant C++ code, and you want to implement exception handling, it is strongly recommended that you use C++ exception handling, rather than structured exception handling. (Note that while the C++ compiler supports structured exception handling constructs as described in these articles, the standard C compiler does not support the C++ exception handling syntax.) For detailed information about C++ exception handling, see C++ Exception Handling and the Annotated C++ Reference Manual by Margaret Ellis and Bjarne Stroustrup. ссылка http://msdn.microsoft.com/en-us/library/7w0chfbk(v=vs.80).aspx ну еще http://msdn.microsoft.com/en-us/library/swezty51(v=vs.80).aspx |
Сообщ.
#5
,
|
|
|
Цитата neokoder @ Это будет уже не ANSI/ISO C И это всё же будет ещё С-программа. |
Сообщ.
#6
,
|
|
|
интересно почему не ловится такое исключение
![]() ![]() try { int x,y,z; x = 1; y = 0; z = x/y; } catch(...) { } |
Сообщ.
#7
,
|
|
|
Цитата Dem_max @ This mechanism automatically handles destruction of local objects during stack unwind. Получается, что скорее всего с любой заданной опцией /EH будут вызываться деструкторы. А если /EH не указано, то не будут. Думаю так. Добавлено Цитата Dem_max @ интересно почему не ловится такое исключение Деление на ноль - это SEH-исключение. А опция компилятора по умолчанию /EHsс. Поставь /EHa и будет ловиться. |
Сообщ.
#8
,
|
|
|
Цитата Деление на ноль - это SEH-исключение. SEH тоже не ловится разными исключениями ловится только если ![]() ![]() float x,y,z; x = 1.0; y = 0.0; z = x/y; |
Сообщ.
#9
,
|
|
|
Цитата Dem_max @ SEH тоже не ловится разными исключениями ловится только если Dem_max, надо в диалоговом окне Exceptions, в разделе Win32 Exceptions поставить галку напротив "Integer division by zero". Вообще это странно почему они не включены по умолчанию! Добавлено Блин а для Release-версии всё рано не ловятся, только для Debug! ![]() |
Сообщ.
#10
,
|
|
|
Отключил оптимизацию, всё заработало для Release-Версии. Что ж это получается если хочешь обрабатыватть исключения в конечной(release) программе, забудь про оптимизацию?
|
Сообщ.
#11
,
|
|
|
Цитата neokoder @ Отключил оптимизацию, всё заработало для Release-Версии. Что ж это получается если хочешь обрабатыватть исключения в конечной(release) программе, забудь про оптимизацию? Да нет, всё там ловится. Оптимизатор просто выбрасывает эти совершенно бессмысленные строки. Сделай так, и всё получится: ![]() ![]() int x,y,z=0; try { x = 1; y = 0; z = x/y; } catch(...) { _tprintf(__TEXT("catch \r\n")); } _tprintf(__TEXT("z=%d\r\n"),z); |
Сообщ.
#12
,
|
|
|
вынос int x,y,z=0; за try не помогает
![]() ![]() int x,y,z = 0; try { x = 1; y = 0; printf("X= %i", x); z = x/y; } catch(...) { puts("Exeption in try"); } |
Сообщ.
#13
,
|
|
|
Цитата Dem_max @ вынос int x,y,z=0; за try не помогает Подумай, что именно ты сделал ? Зачем выносить x,y,z за try ? Текст, который я показал, я откомпилировал. Программа работает как надо. При компиляции выдаётся предупреждение: Цитата warning C4723: potential divide by 0 А у тебя ? |
Сообщ.
#14
,
|
|
|
Цитата А у тебя ? а у меня никакого варнинга |
![]() |
Сообщ.
#15
,
|
|
Цитата neokoder @ Когда-то уже много писал по подобному поводу. Где-то вот отсюда и там ниже на страничке.3) В чём отличие ключа /EHa от отсутствия ключа /EH в принципе? Цитата Dem_max @ С -EHa ловится везде. У тебя z не используется, поэтому компилятор наверняка выкинул код её вычисления. Добавь volatile хотя бы. SEH тоже не ловится |
Сообщ.
#16
,
|
|
|
Проверил с теми опциями котрые доступны в IDE. Исключения в Release версии ловятся только при полном отключении оптимизации.
|
Сообщ.
#17
,
|
|
|
Цитата У тебя z не используется, поэтому компилятор наверняка выкинул код её вычисления. Да правильно, а я не догадался. Но почему тогда не выкидывает при ![]() ![]() float x,y,z; x = 1.0; y = 0.0; z = x/y; |
Сообщ.
#18
,
|
|
|
Цитата Qraizer @ Добавь volatile хотя бы. Если добавить volatile, то всё работает - ловит исключение. но всё же странно почему компилятор вырезает блоки try-catch типа с целью оптимизации! |
![]() |
Сообщ.
#19
,
|
|
Та не блок он вырезал, а вычисление z. Кроме volatile от супероптимизации её вычисления его б остановило объявление z глобальной с внешним связыванием. Когда z локальная, её время жизни полностью под контролем оптимизатора, вот он и берёи на себя больше, чем в этом примере от него требуется.
Dem_max, не знаю. Пути оптимизатора неисповедимы. Наверно не рискнул из-за закулисной жизни _matherr(). |
Сообщ.
#20
,
|
|
|
Понятно, не врубился сразу. С глобальными тоже работает. Про деструкторы по ссылке пока ещё почитаю.
Qraizer вы как считаете использовать лучше try-catch и /EHa или же __try/__except для того чтобы надёжно ловить все исключения(от системы, не программные throw)? |
![]() |
Сообщ.
#21
,
|
|
По try/catch ты не получишь подробной информации о проблеме, если не воспользуешься какой-нибудь прослойкой в лице хотя бы той же _set_se_translator(). Если тебя не интересует эта информация, а интересует только безопасный код, не допускающий утечек ресурсов из-за проблем не в твоём коде, то это не проблема.
По __try/__except ты не получишь информации о C++-проблеме вообще никак, ибо кухня С++ exception handling вообще не стандартизирована и не документирована в отличие от SEH. Опять же это не проблема, если тебя интересует только безопасный код. Для безопасного кода достаточно RAII посредством пар "конструктор/деструктор" и ключика -EHa. Для получения информации о проблеме, если вдруг она-таки потребуется, для C++ достаточно класса для инкапсуляции SEH-контекста и _set_se_translator(). Для того же в случае C надёжного решения нет. Вывод: try/catch предпочтительнее. Однако в функциях, в чьих контексте нет объектов с нетривиальными деструкторами можно и __try/__except и/или __try/__finally, это будет чуть эффективнее. Но в случае появления в будущем в их констекте таких объектов придётся переписывать. |
Сообщ.
#22
,
|
|
|
Цитата Qraizer @ По try/except ты не получишь подробной информации о проблеме, если не воспользуешься какой-нибудь прослойкой в лице хотя бы той же _set_se_translator(). Может вы имели ввиду try/catch? Цитата Qraizer @ По __try/__except ты не получишь информации о C++-проблеме вообще никак, ибо кухня С++ exception handling вообще не стандартизирована и не документирована в отличие от SEH. А какие не мои собственные исключения С++(системные) могут привести к вылету из программы? Цитата Qraizer @ Для безопасного кода достаточно RAII посредством пар "конструктор/деструктор" и ключика -EHa. Будьте добры, что такое RAII? Цитата Qraizer @ Вывод: try/except предпочтительнее. Не понял здесь немного. Вы различаете __except и except? Я в MSDN нашёл информацию только об __except. Может вы имели ввиду try/catch? |
![]() |
Сообщ.
#23
,
|
|
Ну да, try/catch. Перекопипастил.
Поправил. Добавлено Цитата neokoder @ "Не твои собственные", в смысле "не в твоём коде". Свой код ты можешь вылизать в абсолют, хотя бы теоретически. Но когда ты вызываешь не свой код, находящийся в библиотеке не твоего авторства к примеру, с ним ты ничего не сможешь поделать. (Конечно, наличие исключений отнюдь не означает наличие багов, это ещё и может означать плохое тобою чтение документации.) А какие не мои собственные исключения С++(системные) могут привести к вылету из программы? Добавлено Цитата neokoder @ Resource Allocation Is Initialization. Идиома такая. Конструктор и только конструктор захватывает ресурс и объявляет экземпляр класса его владельцем. Деструктор и только деструктор его освобождает. Стандартом языка гарантируются вызовы деструкторов локальных объектов в любом случае, и при обычном завершении функции, и при размотке стека при исключении. Это позволяет резко сократить количество try-блоков в программе и при этом гарантировано не иметь утечек. Эта тема вот тут рассмотрена гораздо подробнее и с примерами. Будьте добры, что такое RAII? |
Сообщ.
#24
,
|
|
|
Для себя решил. Использовать __try, __finally,__leave как наилучший вариант обработки исключительных ситуаций в своём коде. Примером может служить код функции из книги Рихтера:
![]() ![]() DWORD Funcarama4() { // Внимание: инициализируйте все переменные, предполагая худшее HANDLE hFile = INVALID_HANDLE_VALUE; PVOID pvBuf = NULL; // предполагаем, что выполнение функции будет неудачным BOOL bFunctionOk = FALSE; __try { DWORD dwNumBytesRead; BOOL bOk; hFile = CreateFile(TEXT("SOHEDATA.DAT"), GENERIC_READ,FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { __leave; } pvBuf = VirtualAlloc(NULL,1024, MEM_COMMIT, PAGE_READWRITE); if (pvBuf == NULL) { __leave; } bOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL); if (!bOk || (dwNumBytesRead == 0)) { __leave; } // что-то делаем с данными //... // функция выполнена успешно bFunctionOk = TRUE; } __finally { // очистка всех ресурсов if (pvBuf != NULL) VirtualFree(pvBuf,1024,MEM_RELEASE|MEM_DECOMMIT); if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); } // продолжаем что-то делать return(bFunctionOk); } Что касается вызова сторонних функций из библиотек, то для них буду использовать следующий принцип. Программу буду компилировать по умолчанию с ключом (/EHsc) или (/EHs) (в зависимости от задачи). Т.е я не буду ловить структурные исключения c помощью catch. Просто нет желания связываться с _set_se_translator, который необходимо определять для каждого потока, кроме того, если вызывается функция из dll, то она может создавать свой поток и я уже никак не смогу перехватить исключение в этом потоке. Если я не прав поправьте. Поэтому я решил просто использовать 2 функции для вызова стороннего кода- одну для обработки С++ исключений, а вторую для структурных. Т.е. выглядет это так: ![]() ![]() //compile with (/EHsc) int x,y,z; void SafeCall_1(); LONG ExceptFilter (DWORD except_code) { if (except_code==EXCEPTION_INT_DIVIDE_BY_ZERO) { //здесь мы узнаём о произошедшем исключении printf("EXCEPTION_INT_DIVIDE_BY_ZERO\n"); return EXCEPTION_EXECUTE_HANDLER; } else { //здесь мы не обрабатываем те исключения которые система должна решить сама printf("Uknown exception\n"); return EXCEPTION_CONTINUE_SEARCH; } } void UnknownFunction(int what) { x = 1; y = 0; z = x/y; if (what==1) throw (char *)"I am throw an exception"; } void SafeCall() { __try { SafeCall_1(); } __except(ExceptFilter(GetExceptionCode())) { } printf("We don't exit and can continue work\n"); } void SafeCall_1() { try { UnknownFunction(2); } catch(char *mes) { //здесь мы узнаём о произошедшем исключении printf("Unknown function has raised an exception with message: %s\n",mes); } catch(...) { //здесь мы узнаём о произошедшем исключении printf("Unknown function has raised an unknown exception\n"); } } int _tmain(int argc, _TCHAR* argv[]) { SafeCall(); printf("Press any key to close the window..."); _getch(); return 0; } Был бы рад услышать ваши отклики и замечания. Особенно в плане того какие струтурные исключения нельзя ловить, а надо дать системе обработать их самой(т.е. это не означает вылета из программы). Вообще есть такие? |
![]() |
Сообщ.
#25
,
|
|
neokoder, пример Рихтера попадает под упомянутое мною исключение. Она сугубо Сшная, так что Плюсовым try там делать нечего.
Цитата neokoder @ Ну и правильно. Сам так делаю. Это иногда требует некоторой дополнительной ручной работы, но это оправдано. В Плюсовом коде SEH исключений либо нет, т.к. их вычищают, ибо это ошибки, их проще предотвратить проверкой параметров, чем сглаживать последствия от них, либо документированы, а значит не неожиданны, и легко хедлятся индивидуально по месту ещё на стадии программирования. Бывают, конечно, неприятности с багами в библиотеках, но для этого существуют багрепорты их авторам и временные их воркароунды в своём коде. Да и редки они обычно, если библиотека не левый какой-нибудь "компонент". А если нередки, то написать самому обычно дешевле выходит, хотя бы в плане нервов. Что касается вызова сторонних функций из библиотек, то для них буду использовать следующий принцип. ... Добавлено Цитата neokoder @ Эти потоки тебя вообще-то и не касаются. Они вряд ли будут выполнять твой код. А если будут, то и проблемы уже нет, перехватывай. если вызывается функция из dll, то она может создавать свой поток и я уже никак не смогу перехватить исключение в этом потоке Добавлено Цитата neokoder @ Классика. Не надо обрабатывать исключение, если ты не знаешь, что с ним делать. Отправляй дальше, там найдётся кому обработать. Всё, что тебе тут надо - откатиться к своим инвариантам. RAII тут как нельзя кстати - 0 строк кода за исключением кода деструкторов. Ну, или __finally, если это C. Посмотри по той ссылке пример с реализацией SS::operator=(). какие струтурные исключения нельзя ловить, а надо дать системе обработать их самой(т.е. это не означает вылета из программы). |
Сообщ.
#26
,
|
|
|
Цитата Qraizer @ neokoder, пример Рихтера попадает под упомянутое мнорю исключение. Она сугубо Сшная, так что Плюсовым try там делать нечего. Да, а я в принципе С++ функциями редко пользуюсь, разве что классы свои делаю. А для работы с С++ функциями, которые могут дать исключения тот же new я всегда буду заключать в блоки try...catch. Добавлено Цитата Qraizer @ Эти потоки тебя вообще-то и не касаются. Они вряд ли будут выполнять твой код. А если будут, то и проблемы уже нет, перехватывай. Нет, ну как не касаются. Я вызвал функцию из сторонней dll , она создала поток, в потоке произошло исключение которое она не смогла обработать. Всё! Если я пользуюсь _set_se_translator перехватить это исключение я уже не смогу и программа завершится. Вот почему я не желаю пользоваться _set_se_translator. А с помощью 2-х функций такое исключение всегда поймается. Если не прав поправьте. Добавлено Цитата Qraizer @ Идиома такая. Конструктор и только конструктор захватывает ресурс и объявляет экземпляр класса его владельцем. Деструктор и только деструктор его освобождает. Стандартом языка гарантируются вызовы деструкторов локальных объектов в любом случае, и при обычном завершении функции, и при размотке стека при исключении. Ну если честно, ничего не зная о RAII я так всегда и делал ![]() 1) Будет ли гарантированно раскручиваться стек и вызываться деструкторы локальных объектов(классов в основном) после возникновения структурного(SEH) исключения? 2) Если да, то обязательно ли для этого использовать ключ компилятора (/EHa) или можно обойтись(/EHsc)? |
Сообщ.
#27
,
|
|
|
Цитата Qraizer @ Классика. Не надо обрабатывать исключение, если ты не знаешь, что с ним делать. Отправляй дальше, там найдётся кому обработать. Так вот здесь конкретный вопрос. Какие исключения именно для моего кода(не стороннего) из нижеследующего списка мне необходимо принять с EXCEPTION_EXECUTE_HANDLER, и просто ничего не делать, главное чтобы программа продолжала работу, например случайное EXCEPTION_INT_DIVIDE_BY_ZERO, а какие исключения надо отдать системе с помощью EXCEPTION_CONTINUE_SEARCH, но при этом главное чтобы по возможности программа не завершилась, в крайнем случае только тогда, когда исключение ведёт к серьёзному нарушению в программе и её продолжение чревато последствиями. Пусть уж в этом случае создаёт минидамп и отправляет его Microsoft как обычно. ![]() ![]() #define EXCEPTION_ACCESS_VIOLATION STATUS_ACCESS_VIOLATION #define EXCEPTION_DATATYPE_MISALIGNMENT STATUS_DATATYPE_MISALIGNMENT #define EXCEPTION_BREAKPOINT STATUS_BREAKPOINT #define EXCEPTION_SINGLE_STEP STATUS_SINGLE_STEP #define EXCEPTION_ARRAY_BOUNDS_EXCEEDED STATUS_ARRAY_BOUNDS_EXCEEDED #define EXCEPTION_FLT_DENORMAL_OPERAND STATUS_FLOAT_DENORMAL_OPERAND #define EXCEPTION_FLT_DIVIDE_BY_ZERO STATUS_FLOAT_DIVIDE_BY_ZERO #define EXCEPTION_FLT_INEXACT_RESULT STATUS_FLOAT_INEXACT_RESULT #define EXCEPTION_FLT_INVALID_OPERATION STATUS_FLOAT_INVALID_OPERATION #define EXCEPTION_FLT_OVERFLOW STATUS_FLOAT_OVERFLOW #define EXCEPTION_FLT_STACK_CHECK STATUS_FLOAT_STACK_CHECK #define EXCEPTION_FLT_UNDERFLOW STATUS_FLOAT_UNDERFLOW #define EXCEPTION_INT_DIVIDE_BY_ZERO STATUS_INTEGER_DIVIDE_BY_ZERO #define EXCEPTION_INT_OVERFLOW STATUS_INTEGER_OVERFLOW #define EXCEPTION_PRIV_INSTRUCTION STATUS_PRIVILEGED_INSTRUCTION #define EXCEPTION_IN_PAGE_ERROR STATUS_IN_PAGE_ERROR #define EXCEPTION_ILLEGAL_INSTRUCTION STATUS_ILLEGAL_INSTRUCTION #define EXCEPTION_NONCONTINUABLE_EXCEPTION STATUS_NONCONTINUABLE_EXCEPTION #define EXCEPTION_STACK_OVERFLOW STATUS_STACK_OVERFLOW #define EXCEPTION_INVALID_DISPOSITION STATUS_INVALID_DISPOSITION #define EXCEPTION_GUARD_PAGE STATUS_GUARD_PAGE_VIOLATION #define EXCEPTION_INVALID_HANDLE STATUS_INVALID_HANDLE #define EXCEPTION_POSSIBLE_DEADLOCK STATUS_POSSIBLE_DEADLOCK |
Сообщ.
#28
,
|
|
|
Цитата neokoder @ Для себя решил. Использовать __try, __finally,__leave как наилучший вариант обработки исключительных ситуаций в своём коде. Примером может служить код функции из книги Рихтера: А по моему это уже перебор. т.е. этот пример демонстрирует приём работы с __try, __leave, __finally но по сути здесь нет исключений. Гораздо удобнее использовать классы-обьекты содержащие CreateFile, VirtualAlloc, ReadFile и.т.п. и высвобождающие ресурсы в деструкторе. В том случае, если это не удобно делать соотв. методом. |
Сообщ.
#29
,
|
|
|
Цитата ЫукпШ @ А по моему это уже перебор. т.е. этот пример демонстрирует приём работы с __try, __leave, __finally но по сути здесь нет исключений. Ну естественно исключений нет, но появится могут в "любой момент" ![]() Цитата ЫукпШ @ Гораздо удобнее использовать классы-обьекты содержащие CreateFile, VirtualAlloc, ReadFile и.т.п. и высвобождающие ресурсы в деструкторе. В том случае, если это не удобно делать соотв. методом. Ну а вообще, кому что удобнее. ЫукпШ, вы по более важным(для меня) вопросам в моих последних 2-х постах можете что-то сказать? |
Сообщ.
#30
,
|
|
|
Цитата neokoder @ ЫукпШ, вы по более важным(для меня) вопросам в моих последних 2-х постах можете что-то сказать? А я то как раз и ответил на самый важный, на мой взгляд, вопрос. В вышеупомянутом примере имеет место явная попытка скрестить ужа с ежом. Что не нужно. Пусть высвобождением ресурсов занимаются обьекты, а блоки try/canth ловят и обрабатывают исключения. Одно другому не мешает. Добавлено Цитата neokoder @ 1) Будет ли гарантированно раскручиваться стек и вызываться деструкторы локальных объектов(классов в основном) после Да, и информацию об этом легко найти в Сети. тут, например И в книгах, конечно. Добавлено Цитата neokoder @ Так вот здесь конкретный вопрос. Какие исключения именно для моего кода(не стороннего) из нижеследующего списка мне необходимо принять с EXCEPTION_EXECUTE_HANDLER, и просто ничего не делать, главное чтобы программа продолжала работу, например случайное EXCEPTION_INT_DIVIDE_BY_ZERO, а какие исключения надо отдать системе с помощью EXCEPTION_CONTINUE_SEARCH, Об этом может знать только автор конкретной программы - как она должна вести себя в этих случаях. Что касается "отдавать системе", то система завершит аппликэйшын стандартным мессэдж-боксом. На мой взгляд, в необходимом случае лучше показать свой собственный. |
![]() |
Сообщ.
#31
,
|
|
ЫукпШ, думаю, Рихтер подразумевал что в "// что-то делаем с данными" как раз могут быть исключения, особенно, если там вызывается код сторонних библиотек. Даже без учёта этого, очистка, выполняемая в одном месте и запрограммированная один раз, предпочтительнее множественных скопипасченных полуочисток и return в каждом if, а без SEH-блока это только goto или вложенные ifы.
Цитата ЫукпШ @ Вот это в точку. neokoder, ты сам проектируешь дизайн, тебе и определять точки, в которых программа может, перехватив исключение, безопасно продолжить работу. Если у тебя система плагинов, то это будут точки их вызовов, если это сервис, то код обработки запроса от клиентов итп. Как именно реагировать, опять же решать тебе, предварительно почитав описание соответствующих исключений в MSDN и подумав, что тебе конкретно тут предпочтительнее предпринять, может быть в зависмости от конкретного исключения, может быть и независимо от. Об этом может знать только автор конкретной программы - как она должна вести себя в этих случаях. |
Сообщ.
#32
,
|
|
|
Цитата Qraizer @ Даже без учёта этого, очистка, выполняемая в одном месте и запрограммированная один раз, предпочтительнее множественных скопипасченных полуочисток и return в каждом if, а без SEH-блока это только goto или вложенные ifы. Не совсем понял.. Рихтер фактически написал этот текст на С, для демонстрации работы с __try,__finally. Этот приер можно переписать даже так: ![]() ![]() DWORD Funcarama4() { ClassHandle hFile; // класс-хэндл. Но лучше использовать класс-файл ClassVirtualMem pMem; // класс для выделения памяти try { hFile = CreateFile(...); if(hFile == INVALID_HANDLE_VALUE) return ArcticFox; // или даже лучше так // if(!hFile) return ArcticFox; if(!pMem,Allocate(..)) return ArcticFox; if(!ReadFile(...)) return ArcticFox; } catch(...) { return ArcticFox; } // продолжаем что-то делать return bFunctionOk; } |
Сообщ.
#33
,
|
|
|
Мужики, мой вопрос в том что есть такие исключения как EXCEPTION_GUARD_PAGE(для стека) или STATUS_DATATYPE_MISALIGNMENT(актуально для Itanium). Т.е. те проблемы, которые система сама благополучно обработает(устранит) и вернёт в фильтре __except EXCEPTION_CONTINUE_EXECUTION , т.е программа должна продолжиться. Т.е. мне нужно знать те исключения, которые нельзя ни в коем случае обрабатывать с помощью EXCEPTION_EXECUTE_HANDLER.
Цитата Qraizer @ Как именно реагировать, опять же решать тебе, предварительно почитав описание соответствующих исключений в MSDN и подумав, что тебе конкретно тут предпочтительнее предпринять В том то и дело, Qraizer, я не нашёл в MSDN описания по тому или иному SEH исключению когда и почему оно возникает и самое глааное каким образом его система по дефолту обрабатывает? |
![]() |
Сообщ.
#34
,
|
|
neokoder, те исключения, которые система обработает сама, тебе и не передадутся. Сам подумай, иначе бы тебе пришлось к примеру реагировать на каждый page fault. А менеджер виртуальной памяти тогда на что, если его задача быть максимально прозрачным? Вот если ты обратишься к нераспределённому региону памяти, тогда page fault будет передан тебе, потому что в этом случае сам менеджер ещё в ядре исполнит EXCEPTION_CONTINUE_SEARCH. Будь уверен, раз исключение вышло из ядра и дошло до твоих SEH-кадров, оно не обработалось ядром, потому что оно не знает, что с ним делать. Ядро тоже следует принципу
Цитата Qraizer @ Когда оно знает, как поступить с исключением, ты и не подозреваешь, что оно вообще было. Не надо обрабатывать исключение, если ты не знаешь, что с ним делать. Отправляй дальше, там найдётся кому обработать. Добавлено Вообще-то MSDN рассказывает о причинах тех или иных исключений. Может быть не всех, не знаю, тогда прямая дорога к литературе об архитектуре процессора. Другой вопрос, почему оно произошло, и что с ним делать. Но это проблема и с C++-исключениями возникает - обычно если произошло неожидаемое исключение, значит что-то не так с дизайном, и значит надо его пересмотреть и кое-где поправить. В случае с SEH это обычно означает ошибки в программе, и значит нужно найти место, выяснить причину и устранить. Как выяснять и устранять и где удобно перехватывать и безопасно после них восстанавливаться - ну... автору кода лучше знать, готовых рецептов никто не даст. |
Сообщ.
#35
,
|
|
|
Цитата Qraizer @ neokoder, те исключения, которые система обработает сама, тебе и не передадутся. Сам подумай, иначе бы тебе пришлось к примеру реагировать на каждый page fault. Если это так, то это замечательно ![]() Цитата ЫукпШ @ Цитата (neokoder @ 10.04.11, 20:45) 1) Будет ли гарантированно раскручиваться стек и вызываться деструкторы локальных объектов(классов в основном) после Да, и информацию об этом легко найти в Сети. тут, например И в книгах, конечно. Сергей, я задавал вопрос двойной не случайно: Цитата 1) Будет ли гарантированно раскручиваться стек и вызываться деструкторы локальных объектов(классов в основном) после возникновения структурного(SEH) исключения? 2) Если да, то обязательно ли для этого использовать ключ компилятора (/EHa) или можно обойтись(/EHsc)? Если вы утвердительно ответили, то должен сказать, что ДА, но только для ключа /EHa. Я проверил. C ключами /EHs или /EHsc деструкторы не будут вызываться до тех пор пока исключение не будет обработано с помощью __except, а это значит что в той функции которая вызвала исключение все локальные объекты точно не будут освобождены, потому как туда добавить __try/__except не позволит компилер. Я вот написал два варианта обработки исключений для программы, работающей 24x7, первый вариант мой, второй вроде этот RAII как говорил Qraizer. Сразу скажу что я ошибся в том, что мой вариант может поймать необработанно исключение в другом потоке(создать новый поток может,например, сторонняя библиотека, которую мы используем в своей программе). Поймать такое исключение невозможно. Поэтому вся надежда на то что сторонняя программа будет корректно работать ![]() Вообще надеяться на то что деструкторы объектов будут обязательно вызваны в случае возникновения исключения мне кажется слишком рисковано. Потому как вызов деструкторов возможен только для объектов находящихся в стеке, т.е. нельзя например выделить память для класса динамически с помощью new и надеяться что система вызовет деструктор, его придётся вызывать явно. Я в своих программах всегда выделяю память под объекты классы и структуры динамически. А значит ключ /EHa и RAII мне не подойдёт поскольку он не позволит использовать __try/__finally/__except там где под объекты память выделяется динамически. Если полагаться только на деструкторы(2 вариант), то на мой взгляд есть ряд неудобств: 1) В программе где есть только классы это может быть удобно, но если дополнительно необходимо просто выделить динамически массив или какие-то объекты с помощью new, malloc и др., то это уже невозможно, компилятор будет писать ошибку "Невозможно использовать __try в функциях, требующих уничтожения объектов". Не использовать __finally нельзя поскольку при возникновении исключения будет утечка памяти. 2)Чтобы получить код SEH исключения нужно использовать _set_se_translator для каждого потока приложения(в приницпе для 1 варианта также необходимо блоки __try/__except для каждого нового потока). Но использование _set_se_translator не позволит задавать ни EXCEPTION_CONTINUE_SEARCH, ни EXCEPTION_CONTINUE_EXECUTION. 3) Кроме того расход памяти стека может привести к его нехватке, особенно если в программе всего один поток(и значит стек). По мне так привычнее следующий принцип, который кстати и демонстрируется Рихтером. Не важно какой объект и как мы создаём, класс или WinAPI-вызовом или ещё какой либо функцией, правило одно: каждый созданный объект должен иметь свой __finally(обычно в одном __finally разрушается несколько объектов для удобства). Т.е. всё что необходимо это дополнять каждое создание объекта его разрушением в __finally. Вариант№1 ![]() ![]() //compile without (/EH) #include <stdio.h> #include <exception> #include <conio.h> #include <tchar.h> #include <process.h> #include <windows.h> typedef unsigned (__stdcall *PTHREAD_START) (void *); #define my_beginthreadex(psa, cbStack, pfnStartAddr, pvParam, fdwCreate, pdwThreadID)((HANDLE) _beginthreadex((void *) (psa),(unsigned) (cbStack),(PTHREAD_START) (pfnStartAddr),(void *) (pvParam),(unsigned) (fdwCreate),(unsigned *) (pdwThreadID))) //------------------------------------------------------------------------------------- class Test { public: int num; static int counter; Test() { num=counter++; printf("Constructor %d done.\n",num); } ~Test() { printf("Destructor %d done.\n",num); } }; int Test::counter=1; //------------------------------------------------------------------------------------- //глобальные переменные int x,y,z; int a,b,c; volatile BOOLEAN is_lastexceptSEHsys; HANDLE hTestThread; //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- unsigned __stdcall TestThread(void *pvParam) { printf("Test thread started\n"); Sleep(5000); //SEH, деление на ноль a = 1; b = 0; c = a/b; printf("Test thread finished\n"); return 0; } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- LONG MyCode_ExceptFilter(DWORD except_code,PEXCEPTION_POINTERS except_info) { if (0x20000000&except_code) is_lastexceptSEHsys=false; else is_lastexceptSEHsys=true; //здесь заносим в лог-файл произошедшее исключение, для примера просто печатаю printf("Date/Time Exception: 0x%X; Address: 0x%p\n",except_code,except_info->ExceptionRecord->ExceptionAddress); //отдаём системе на обработку //if (is_lastexceptSEHsys) return EXCEPTION_CONTINUE_SEARCH; //else //return EXCEPTION_EXECUTE_HANDLER; } //------------------------------------------------------------------------------------- void UnknownFunction1_2(int what) { Test *test4=NULL; DWORD dwThreadID; test4=new Test(); __try { //запускаем поток, который может вызвать исключение /* hTestThread=my_beginthreadex(NULL,0,&TestThread,NULL,0,&dwThreadID); if (hTestThread==NULL) { printf("Error: can't start testing thread.\n"); return; } CloseHandle(hTestThread);*/ //исключение bad:alloc (С++ исключение) // __int64 *pI = new __int64[0xFFFFFFF]; //SEH исключение(деление на ноль) x = 1; y = 0; z = x/y; //пользовательское исключение //if (what==1) // throw (char *)"I am throw an exception"; } __finally { if (test4) delete test4; } } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- // Данная функция может вызывать другие функции, но принцип остаётся один: // дополнять каждое создание объекта его разрушением в __finally. //------------------------------------------------------------------------------------- void UnknownFunction1_1(int what) { Test *test3=NULL; test3=new Test(); __try { UnknownFunction1_2(1); //исключение bad:alloc (С++ исключение) //__int64 *pI = new __int64[0xFFFFFFF]; //SEH, деление на ноль //x = 1; //y = 0; //z = x/y; //пользовательское исключение //if (what==1) // throw (char *)"I am throw an exception"; } __finally { if (test3) delete test3; } } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- // Функция1. Сначала ловим любое SEH-исключение с корректным освобождением всех объектов // и занесением в лог-файл записи о возникновении исключения. // // компилировать без (/EH), чтобы работал __finally для уничтожения объектов, // деструкторы не вызываются автоматически, но все __finally будут выполнены. // __except необходим чтобы фиксировать произошедшие исключения void MyCode_SafeCall_1() { Test *test2=NULL; test2=new Test(); //далее выполняется неизвестная функция, неизвестно какое оно даст исключение __try { __try { //здесь выполняется основная работа в программе //вызываются различные функции, которые в свою очередь могут вызывать другие //... UnknownFunction1_1(1); } __finally { if (test2) delete test2; } } __except(MyCode_ExceptFilter(GetExceptionCode(),GetExceptionInformation())) { //здесь можно обработать те SEH-исключения и пользовательские, которые мы хотим обработать //также здесь можно было бы ловить C++ исключения, но информация по ним доступна только с помощью catch, //поэтому они обрабатываются в catch printf("Here we can proceed all exceptions what we need\n"); } } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- //разрушает все глобальные объекты void ShutdownProgram() { //здесь сохраняем, освобождаем и закрываем всё что необходимо //... } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- // Функция 2. Её основная задача поймать С++-исключение, чтобы работа продолжилась если ошибка устранима // и корректное закрытие программы с записью в лог-файл в случае фатальной ошибки. // но ловит она все исключения поскольку так просто удобнее - всё в одном месте BOOLEAN MyCode_SafeCall() { try { MyCode_SafeCall_1(); } //первые catch должны будут ловить все известные наши собственные исключения(если понадобятся) //продолжать работу программы или нет вопрос на усмотрение разработчика catch(char *mes) { //запись в лог файл о возникшем C++ исключении printf("Date/Time Exception with message: %s\n",mes); } //catch(std::bad_alloc &) //{ // //запись в лог файл о возникшем C++ исключении // printf("Unknown function has raised an bad_alloc exception\n"); //} catch(std::exception &except) { //запись в лог файл о возникшем C++ исключении printf("Date/Time std::exception with message: %s\n",except.what()); //лучше корректно закрыть программу, иначе могут быть непредсказуемые результаты ShutdownProgram(); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } catch(...) { //если исключение системное, здесь мы завершаем работу, но главное корректно, фиксируя всю необходимую информацию, //и с сообщением об ошибке if (is_lastexceptSEHsys) { ShutdownProgram(); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } else { //здесь есть варианты, как поступиить если исключение проскочило, а у нас нет выше кэтчера для его обработки //лучше корректно закрыть программу, иначе могут быть непредсказуемые результаты //запись в лог файл о возникшем C++ исключении или несистемном SEH-исключении printf("Date/Time Unknown C++ exception or not system SEH exception\n"); ShutdownProgram(); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } } printf("We don't exit and can continue work\n"); return true; } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- int _tmain(int argc, _TCHAR* argv[]) { DWORD ret_code=0; Test *test1=NULL; test1=new Test(); //пример довольно избыточный, но показывающий, что обработка может быть вне главной функции main //можно было бы поставить здесь "верхнюю"(с помощью try/catch) обработку исключений из функции MyCode_SafeCall __try { if (!MyCode_SafeCall()) { ret_code=1; __leave; } //ждём здесь для проверки исключения в потоке //printf("Press any key to close the window..."); //_getch(); } __finally { if (test1) delete test1; } if (ret_code!=0) return ret_code; //если функция выполнилась успешно, то продолжаем выполнять программу //............ printf("Press any key to close the window..."); _getch(); return ret_code; } //------------------------------------------------------------------------------------- Вариант №2. Вроде бы RAII, если нет то скажите почему. ![]() ![]() //Компилировать с ключом /EHa #include <stdio.h> #include <exception> #include <conio.h> #include <tchar.h> #include <process.h> #include <windows.h> typedef unsigned (__stdcall *PTHREAD_START) (void *); #define my_beginthreadex(psa, cbStack, pfnStartAddr, pvParam, fdwCreate, pdwThreadID)((HANDLE) _beginthreadex((void *) (psa),(unsigned) (cbStack),(PTHREAD_START) (pfnStartAddr),(void *) (pvParam),(unsigned) (fdwCreate),(unsigned *) (pdwThreadID))) //------------------------------------------------------------------------------------- class SE_Exception { public: EXCEPTION_POINTERS pExceptionInfo; SE_Exception(EXCEPTION_POINTERS *pExceptInfo) { pExceptionInfo=*pExceptInfo; } ~SE_Exception() { /*printf("SE_Exception destructor\n");*/ } }; //------------------------------------------------------------------------------------- class Test { public: int num; static int counter; Test() { num=counter++; printf("Constructor %d done.\n",num); } ~Test() { printf("Destructor %d done.\n",num); } }; int Test::counter=1; //------------------------------------------------------------------------------------- //глобальные переменные int x,y,z; int a,b,c; volatile BOOLEAN is_lastexceptSEHsys; HANDLE hTestThread; //------------------------------------------------------------------------------------- unsigned __stdcall TestThread(void *pvParam) { printf("Test thread started\n"); Sleep(5000); //SEH, деление на ноль a = 1; b = 0; c = a/b; printf("Test thread finished\n"); return 0; } //------------------------------------------------------------------------------------- void trans_func( unsigned int u, EXCEPTION_POINTERS* pExceptionInfo ) { //printf( "In trans_func.\n" ); throw SE_Exception(pExceptionInfo); } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- void UnknownFunction2_2(int what) { Test test4; DWORD dwThreadID; /* hTestThread=my_beginthreadex(NULL,0,&TestThread,NULL,0,&dwThreadID); if (hTestThread==NULL) { printf("Error: can't start testing thread.\n"); return; } CloseHandle(hTestThread);*/ //исключение bad:alloc (С++ исключение) //__int64 *pI = new __int64[0xFFFFFFF]; //SEH, деление на ноль x = 1; y = 0; z = x/y; //пользовательское исключение //if (what==1) // throw (char *)"I am throw an exception"; } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- // Данная функция может вызывать другие функции //------------------------------------------------------------------------------------- void UnknownFunction2_1(int what) { Test test3; UnknownFunction2_2(1); //исключение bad:alloc (С++ исключение) //__int64 *pI = new __int64[0xFFFFFFF]; //SEH, деление на ноль /*x = 1; y = 0; z = x/y;*/ //пользовательское исключение /*if (what==1) throw (char *)"I am throw an exception";*/ } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- void MyCode2_SafeCall_1() { Test test2; //далее выполняется неизвестная функция, неизвестно какое оно даст исключение //здесь выполняется основная работа в программе //... UnknownFunction2_1(1); } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- BOOLEAN MyCode2_SafeCall() { try { MyCode2_SafeCall_1(); } //первые catch должны будут ловить наши собственные исключения(если понадобится) //продолжать работу программы или нет вопрос на усмотрение разработчика catch(char *mes) { //запись в лог файл о возникшем C++ исключении printf("Date/Time Exception with message: %s\n",mes); } catch(std::exception &except) { //запись в лог файл о возникшем C++ исключении printf("Date/Time std::exception with message: %s\n",except.what()); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } catch(SE_Exception &seh) { //запись в лог файл о возникшем SEH исключении printf("Date/Time Exception: 0x%X; Address: 0x%p\n",seh.pExceptionInfo.ExceptionRecord->ExceptionCode,seh.pExceptionInfo.ExceptionRecord->ExceptionAddress); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } catch(...) { //запись в лог файл о возникшем C++ исключении printf("Date/Time Unknown exception\n"); MessageBox(NULL,_T("Программа вызвала исключение и будет закрыта. В лог-файл записана информация о возникшем исключении."), _T("Неустранимая ошибка в программе"),MB_OK|MB_ICONERROR); return false; } printf("We don't exit and can continue work\n"); return true; } //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- int _tmain(int argc, _TCHAR* argv[]) { Test test1; //назначаем функцию для SEH исключений _set_se_translator(trans_func); if (!MyCode2_SafeCall()) return 1; printf("Press any key to close the window..."); _getch(); return 0; } //------------------------------------------------------------------------------------- Ещё хотелось бы при возникновении исключения у клиента на комппе записать фрагмент кода, где оно произошло, чтобы легче было найти ошибку. Вот только по адресу который даёт функция GetExceptionInformation() в параметре ExceptionAddress(это по сути значение регистра EIP) я не могу найти свой код. Почему? Знаю, что там надо какое-то преобразование делать, но я не силён в ассемблере. Может кто поможет? |
Сообщ.
#36
,
|
|
|
Цитата Этот приер можно переписать даже так: не все можно переписать так. Иногда WinAPI функция может вызвать исключенние. |
![]() |
Сообщ.
#37
,
|
|
Цитата neokoder @ Безусловно. Хип для того и придуман, чтобы объекты в нём имели глобальное время жизни. Кому нужен объект в хипе, который удаляется по выходу из функции? А иначе с чего он должен удаляться при исключении? Потому как вызов деструкторов возможен только для объектов находящихся в стеке, т.е. нельзя например выделить память для класса динамически с помощью new и надеяться что система вызовет деструктор, его придётся вызывать явно. Я в своих программах всегда выделяю память под объекты классы и структуры динамически. Я забыл, когда последний раз пользовался сырыми указателями для хранения динамических объектов. Либо std::vector<> для массивов, включая простые типы, либо std::auto_ptr<> для одиночных объектов. В ряде случаев boost::shared_ptr<>, но это для более сложных случаев с динамическими объектами, нежели обычное локальное размещение с RAII. Цитата neokoder @ Тоже безусловно. Его задача - преобзаровать SEH в C++ EH, откуда там EXCEPTION_CONTINUE_SEARCH или EXCEPTION_CONTINUE_EXECUTION? Первое определяется типом исключения, покидающего функцию трансляции, и стеком catch-блоков, второе в принципе невозможно. Попробуй придумать логический сценарий, когда после исключения нужно было бы продолжить исполнение. EXCEPTION_CONTINUE_EXECUTION используется настолько редко, что обоснованность его наличия вообще вызывает определённое сомнение, если только речь не идёт об очень низкоуровневых приложениях, типа отладчиков или тестирующих и других системых инструментов. Не даром в C++ EH нет его аналога.Но использование _set_se_translator не позволит задавать ни EXCEPTION_CONTINUE_SEARCH, ни EXCEPTION_CONTINUE_EXECUTION. P.S. Ничего особенного в коде Рихтера не увидел. |
Сообщ.
#38
,
|
|
|
Цитата neokoder @ А значит ключ /EHa и RAII мне не подойдёт поскольку он не позволит использовать __try/__finally/__except там где под объекты память выделяется динамически. Эта проблема легко обходится. Кроме того, VC не запрещает использовать /EHa, try/catch и __try/__except вместе. ![]() Добавлено Цитата neokoder @ 1) В программе где есть только классы это может быть удобно, но если дополнительно необходимо просто выделить динамически массив или какие-то объекты с помощью new, malloc и др., то это уже невозможно, компилятор будет писать ошибку "Невозможно использовать... Ты и сам легко можешь решить такую проблему и навсегда избавиться от такой необходимости. ![]() Добавлено Цитата neokoder @ Ещё хотелось бы при возникновении исключения у клиента на комппе записать фрагмент кода, где оно произошло, чтобы легче было найти ошибку. Вот только по адресу который даёт функция GetExceptionInformation() в параметре ExceptionAddress(это по сути значение регистра EIP) я не могу найти свой код. Почему? Знаю, что там надо какое-то преобразование делать, но я не силён в ассемблере. Может кто поможет? Посмотри примерчик. Возможно, там не всё идеально. А где ты ищешь и не можешь найти свой код ? Прикреплённый файл ![]() |
Сообщ.
#39
,
|
|
|
Цитата ЫукпШ @ Эта проблема легко обходится. Нет ну понятно что всё можно обойти. Я понял как вы делаете - дополнительная функция SomeFunc которая ловит специально SEH-исключения. В этой функции объекты не создаются, их невозможно создать - компилер не позволит. Об этом я и говорил. И вам придётся писать универсальную функцию SomeFunc через которую вызывать ваши функции, чтобы обрабатывать SEH-исключения. Цитата Кроме того, VC не запрещает использовать /EHa, try/catch и __try/__except вместе. Через создание доп. функций да, но не в одной функции. Говоря о невозможности я именно это имел ввиду - в пределах одной функции. Цитата ЫукпШ @ Ты и сам легко можешь решить такую проблему и навсегда избавиться от такой необходимости. В вашей функции SomeFunc, например, вы не сможете создать ни одного объекта. Только об этом я и говорил. Вообще вариантов больше конечно же чем те 2, коорые я привёл. Как кому удобнее так тот и делает. Цитата ЫукпШ @ Посмотри примерчик. Посмотрел, спасибо. Вы можете обнаружить функцию, в которой произошло исключение с помощью добавления в каждую функцию блоков try/catch. Я хотел что-то более универсальное - а именно сдампить участок памяти где произошло исключение, т.е получить дамп кода. Цитата ЫукпШ @ А где ты ищешь и не можешь найти свой код ? В принципе я уже понял, что я не мог найти. Когда вызывается оператор new и происходит исключение bad_alloc система залезает куда-то очень далеко, в свои дебри. А нужная строчка кода с оператором new как бы скрывается. Такие исключения видимо действительно можно локализовать толко в пределах одной функции, например, как это делал ты. Другие исключения, например, деление на ноль даёт непосредственно адрес инструкции кода моей программы, где это исключение произошло, т.е. адрес инструкции IDIV. Для этих исключений получить дамп кода не составляет проблем. Добавлено Цитата Qraizer @ Кому нужен объект в хипе, который удаляется по выходу из функции? Мне например нужен. Функции разные бывают, например основная функция дополнительного потока. И по завершении работы потока объекты должны быть уничтожены. И не обязательно объекты в динамической памяти, это могут быть объекты созданные WinAPI функциями и их тоже надо закрывать/уничтожать. Добавлено Цитата Qraizer @ Попробуй придумать логический сценарий, когда после исключения нужно было бы продолжить исполнение. Пожалуйста, создание таблицы ячеек и выделение физической памяти по мере необходимости. Пример есть в книге Рихтера. |
Сообщ.
#40
,
|
|
|
Цитата neokoder @ Нет ну понятно что всё можно обойти. Я понял как вы делаете - дополнительная функция SomeFunc которая ловит специально SEH-исключения. Не совсем так. я полагаю, что /EHa + try/catch практически достаточно при широком употреблении. Но иногда хочется получить адрес и контекст. Для этих целей можно использовать небольшое количество блков __try/__except, заключающих в свои обьятия крупные участки кода. Возможно, достаточно даже одного такого блока. Или ни одного - можно вообще такие блоки не использовать, а переопределить ExceptionFilter и именно из фильтра писать в лог контекст. |
![]() |
Сообщ.
#41
,
|
|
Цитата neokoder @ Естественно разные. И я ж об этом. Но является ли созданный объект локальным или глобальным, хип не знает. И компилятор тоже не знает. Только ты знаешь. Для локальных используй кассы с владением ресурсов, в частности для ресурсов динамической памяти - смарт-поинтеры, и будет счастье.Мне например нужен. Функции разные бывают, например основная функция дополнительного потока. Цитата neokoder @ Хм... Мы говорим о неожидаемых исключениях или об исключениях как часть бизнес-логики? Если ты ожидаешь исключения, о какой защите от них тогда речь? Пиши функцию, где-нибудь высоко к main(), и там в __except-фильтре фильтруй свою логику на здоровье, зачем тут тебе SEH транслировать? И ЫукпШ об этом же. Впрочем, ты можешь воспользоваться VEH, Vectored Exception Handling. создание таблицы ячеек и выделение физической памяти по мере необходимости. И кстати, для SE-трансляторов также имеет смысл писать классы владения, ставя т.о. их на обработку локально в определённых фунцкиях и восстанавливая предыдущие фильтры по выходу из них. |
Сообщ.
#42
,
|
|
|
Ладно, мужики, спасибо. Я же говорю, кому как удобнее тот так и делает. Цель то одна - кооректное освобождение всех ресурсов при возникновении исключения и запись его в лог-файл. А вариантов реализации несколько.
Я получил ответы на все свои вопросы и даже больше, так что тему можно закрывать. А вам спасибо, подниму и без того огромный ваш рейтинг ![]() |
Сообщ.
#43
,
|
|
|
Почитайте заметку: Возможен ли вызов исключения? Думаю, инфа будет полезна.
|