Переключение на Главную Страницу Страницы: 1 ОтправитьПечать
Обычная тема Исключения: SEH и С++ (число прочтений - 3445 )
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Исключения: SEH и С++
30. Октября 2007 :: 11:46
Печать  
Если кто-то сегодня утром проснулся, и подумал, что понедельник миновал, а остальное все слишком просто в этой жизни, то этой беде я могу помочь.
Есть одна тема - задумался я над концептуальной основой бытия, выраженной в мире программных исключений.

Итак, у нас есть исключения C++

try
{ // something useful }
catch(exception& e)
{}
catch(...)
{  // point X }

Проблема 1: как в точке 'X' определить тип исключения?
Штатных методов не знаю, нештатные более-менее понятны, но компиляторо-зависимы и однако же совсем не просты.
С другой стороны, под конкретный компилятор определялку написать можно, но надо время и копацца в байтах.
Если кому-то попадалась что-то готовое, я бы лучше пива выпил.

Проблема 2: неплохо бы ловить асинхронные (SEH) исключения через механизм C++ исключений
При этом трюк с _set_se_translator понятен, как и ключ msvc/icl /EHa.

Однако ж интересно, без установки транслятора что там ловится, в этом catch(...){} (а оно ловится в случае с /EHa)
И можно ли в этом случае получить информацию о коде ошибки SEH (т.е. то, что доступно через механизм обработки исключений SEH).

Кроме того, опять же, _set_se_translator действует в пределах потока.
Т.е. это что, если мы попытаемся использовать эту методу в нескольких независимых dll, мы видимо получим конфликт?



А цель-то вообщем простая - ловить адресно, не прибегая к ковровым бомбардировкам.
Поэтому для C++ исключений в итоге будет переносимо, ибо определив тип прилетающего исключения, я пропишу для него отдельный блок.

А вот как сделать переносимым и бесконфликтным обработку SEH в пределах приложения, собранного из библиотек разных вендоров, пока не очень понятно.
  

De quelle planète es-tu?
Наверх
 
IP записан
 
Uzhast
1c++ power user
Отсутствует



Сообщений: 1341
Зарегистрирован: 30. Августа 2006
Пол: Мужской
Re: Исключения: SEH и С++
Ответ #1 - 30. Октября 2007 :: 18:57
Печать  
kms писал(а) 30. Октября 2007 :: 11:46:
Итак, у нас есть исключения C++

try
{ // something useful }
catch(exception& e)
{}
catch(...)
{  // point X }

Проблема 1: как в точке 'X' определить тип исключения?
Штатных методов не знаю, нештатные более-менее понятны, но компиляторо-зависимы и однако же совсем не просты.
С другой стороны, под конкретный компилятор определялку написать можно, но надо время и копацца в байтах.
Если кому-то попадалась что-то готовое, я бы лучше пива выпил.

Вот попалась любопытственная ссылка на RSDN: http://rsdn.ru/Forum/Message.aspx?mid=1029176&only=1
Но, естественно, совсем любые исключения детектить этот метод не позволит. Только те, которые возникают в доступных для модификации сырцах.

kms писал(а) 30. Октября 2007 :: 11:46:
Однако ж интересно, без установки транслятора что там ловится, в этом catch(...){} (а оно ловится в случае с /EHa)
И можно ли в этом случае получить информацию о коде ошибки SEH (т.е. то, что доступно через механизм обработки исключений SEH).

ИМХО, как-то смысла в такой информации мало. Вернее, в способе получения этой информации. Все равно не будет переносимого способа. Все равно будет привязка к платформе и компилятору. Так зачем перегружать для этого стандартный механизм исключений, если сразу можно задействовать платформозависимые средства типа сехов? Как-то логичнее платформозависимые косяки ловить платформозависимыми методами Улыбка

kms писал(а) 30. Октября 2007 :: 11:46:
А цель-то вообщем простая - ловить адресно, не прибегая к ковровым бомбардировкам.
Поэтому для C++ исключений в итоге будет переносимо, ибо определив тип прилетающего исключения, я пропишу для него отдельный блок.

А, может, отладчика хватит? Улыбка Студийный отладчик хорошо определяет типы исключений. (Причем, похоже, в VS6 он этого еще не умел) Улыбка Тебе ведь все равно тип отловленного исключения нужен, чтобы встроить соответствующий блок. Поэтому, получается, способ "проведения исследования" не так уж и важен Улыбка

kms писал(а) 30. Октября 2007 :: 11:46:
А вот как сделать переносимым и бесконфликтным обработку SEH в пределах приложения, собранного из библиотек разных вендоров, пока не очень понятно.

Я так понимаю, если транслировать сехи в C++ исключения, то конфликты могут быть - из-за различий в реализации поддержки исключений в разных модулях. А если не вмешивать в это дело C++? Не будет ли так лучше?
  
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: Исключения: SEH и С++
Ответ #2 - 30. Октября 2007 :: 23:01
Печать  
Uzhast писал(а) 30. Октября 2007 :: 18:57:
Вот попалась любопытственная ссылка на RSDN: http://rsdn.ru/Forum/Message.aspx?mid=1029176&only=1
Но, естественно, совсем любые исключения детектить этот метод не позволит. Только те, которые возникают в доступных для модификации сырцах.

О, классно, спасибо.
Я утром на rsdn видел, как кто-то эту ветку искал, да сама не попалась.

Цитата:
ИМХО, как-то смысла в такой информации мало. Вернее, в способе получения этой информации. Все равно не будет переносимого способа. Все равно будет привязка к платформе и компилятору. Так зачем перегружать для этого стандартный механизм исключений, если сразу можно задействовать платформозависимые средства типа сехов? Как-то логичнее платформозависимые косяки ловить платформозависимыми методами Улыбка

Дык беда в том, что в одной функции нельзя использовать __try/__except и try/catch одновременно.
А это иногда совсем не круто.
И в принципе, это удобно, когда в едином стиле ловятся и деление на ноль и bad_alloc.
Они же ничем не отличаются, если не смотреть сквозь дыры абстракций.

kms писал(а) 30. Октября 2007 :: 11:46:
А, может, отладчика хватит? Улыбка Студийный отладчик хорошо определяет типы исключений. (Причем, похоже, в VS6 он этого еще не умел) Улыбка Тебе ведь все равно тип отловленного исключения нужен, чтобы встроить соответствующий блок. Поэтому, получается, способ "проведения исследования" не так уж и важен Улыбка

Согласен, и очень доволен vs8 отладчиком в этом плане. Улыбка
Но захотелось разобраться, как он это делает и ничего ли не утаивает Улыбка

Цитата:
kms писал(а) 30. Октября 2007 :: 11:46:
А вот как сделать переносимым и бесконфликтным обработку SEH в пределах приложения, собранного из библиотек разных вендоров, пока не очень понятно.

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


С транслятором, в принципе, я вижу только одну проблему.
Предположим, мы с тобой пишем по модулю.
Ты используешь у себя _set_se_translator и я тоже.
При этом для деления на ноль ты бросаешь какой-нибудь CDivisionByZero, а я бросаю CVeryBadDivision.
В итоге либо в один, либо в другой модуль будут прилетать чужие исключения, что, я думаю, отрицательно скажется на кислотно-щелочном балансе.

А так все довольно готичненько. Улыбка


Кстати, вот еще одна обалденная статья на тему реализации обработки исключений под windows.
http://www.codeproject.com/cpp/Exceptionhandler.asp?df=100&forumid=3666&exp=0&se...

Большая часть соответствует действительности.

И, в принципе, довольно легко оказалось определить тип исключения в блоке catch(...)
Если рассматривать блок catch(...) как некую функцию, то она имела бы сигнатуру на моей системе

void* __cdecl catch_and_return_next_eip(
         _EXCEPTION_RECORD *pexc_rec,
         EXCEPTION_REGISTRATION  *preg_frame)

Отсюда много чего можно получить, и все то, что мне нужно было там тоже есть.
В EXCEPTION_REGISTRATION для SEH исключений есть код и адрес исключения, а для cpp исключений есть сам объект исключения, да если захотеть, то и type_info для этого объекта можно найти через структуру excpt_info.

Короче, если бы отладчик по-прежнему ничего не умел, я бы щаз блеснул.
А так все же пойду пиво пить. Улыбка
  

De quelle planète es-tu?
Наверх
 
IP записан
 
Uzhast
1c++ power user
Отсутствует



Сообщений: 1341
Зарегистрирован: 30. Августа 2006
Пол: Мужской
Re: Исключения: SEH и С++
Ответ #3 - 31. Октября 2007 :: 18:08
Печать  
kms писал(а) 30. Октября 2007 :: 23:01:
Дык беда в том, что в одной функции нельзя использовать __try/__except и try/catch одновременно.
А это иногда совсем не круто.
И в принципе, это удобно, когда в едином стиле ловятся и деление на ноль и bad_alloc.
Они же ничем не отличаются, если не смотреть сквозь дыры абстракций.

Ну как сказать... Отличаются тем, что сехи не стандартизованы. Вот разве что транслировать сехи на разных платфорах в одинаковый класс исключения... Например, что в Windows на Access Violation, что в Линухе на segfault выкидывать BadHandsException Улыбка

Мне кажется основное применение сехов - это сделать минидамп и предложить юзверю отправить его с отчетом по нужному мылу. Ну как истории про "Сортир Windows XP": ##па пользователя отрезается и отсылается производителю на предмет выяснения причины неполадок Улыбка

Ведь после возникновения сеха, как правило, сделать уже почти ничего нельзя. Вернее, если продолжать работать, как ни в чем не бывало, то, скорее всего, будет еще хуже. Поэтому и смешивание исключений и сехов - ИМХО, уж очень нехорошо смотрится. Исключение - просто способ передачи управления, а win32-исключение - что-то почти всегда фатальное. Если смотреть с такой точки зрения, то мест для вставляния отлова сехов получается немного - где-нибудь в точках входа в модуль. Накрайняк можно сделать две функции вместо одной  Очень довольный

kms писал(а) 30. Октября 2007 :: 23:01:
С транслятором, в принципе, я вижу только одну проблему.
Предположим, мы с тобой пишем по модулю.
Ты используешь у себя _set_se_translator и я тоже.
При этом для деления на ноль ты бросаешь какой-нибудь CDivisionByZero, а я бросаю CVeryBadDivision.
В итоге либо в один, либо в другой модуль будут прилетать чужие исключения, что, я думаю, отрицательно скажется на кислотно-щелочном балансе.

Прилетать в другой модуль чужие исключения могут только в случае, когда CRT у них совместимы и генерируемый код таков, что CRT правильно может сделать раскрутку стека. С VC6 и IC проблем нет. Потому что Интел такой, сцуко, совместимый, что просто диву даешься Улыбка А вот если втюхать в приложение какой-нибудь модуль, например, на g++ (или еще на чем-нибудь таком), то при вылете исключения оттуда, ИМХО, будет полный ахтунг. И исключение наверное быстро превратится в win32-исключение - и это еще будет хорошо Улыбка Так что лучше, наверное, делать как в лучших домах ЛондОна и ПарижА - исключения за пределы модуля не выпускать вообще Улыбка
  
Наверх
 
IP записан
 
kms
1c++ power user
1c++ moderator
Отсутствует


я хочу, чтоб сюда проложили
дорогу оттуда...

Сообщений: 4632
Зарегистрирован: 19. Мая 2006
Re: Исключения: SEH и С++
Ответ #4 - 31. Октября 2007 :: 20:27
Печать  
Это да.

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

Цитата:
Ну как сказать... Отличаются тем, что сехи не стандартизованы. Вот разве что транслировать сехи на разных платфорах в одинаковый класс исключения... Например, что в Windows на Access Violation, что в Линухе на segfault выкидывать BadHandsException Улыбка

О. Отличное название. Подождите, я записываю Улыбка
Ну типа да, идея в том, чтобы сделать непереносимый платформозависимый модуль транслятора SEH, который бросает нормальные исключений С++ (а их обработка уже переносима).

Цитата:
Прилетать в другой модуль чужие исключения могут только в случае, когда CRT у них совместимы и генерируемый код таков, что CRT правильно может сделать раскрутку стека. С VC6 и IC проблем нет.

Тут меня печалит несколько другой момент.
Предположим, мы в двух совместимых модулях независимо используем _set_se_translator
При этом в одном модуле пишем
Код
Выбрать все
static void se_translator(unsigned int code,_EXCEPTION_POINTERS *)
{
if (code == EXCEPTION_FLT_DIVIDE_BY_ZERO)
   throw CDivideByZero();
}
дальше где-то
try
{
// something useful
}
catch(CDivideByZero& e)
{
// handler
}
 



В другом модуле - все то же самое, но не CDivideByZero, а какой-нибудь CFckngDiv.

В итоге, модуль, который загружается вторым, переопределяет se_translator на свой, и в первый модуль начинают прилетать не CDivideByZero, а CFckngDiv, что соответствующим образом влияет на общий анамнез.

Есть вероятность, что здесь есть какие-то тонкости, но на первый взгляд этот механизм, либо надо использовать единолично, когда больше никто не использует, либо использовать один se_translator на все модули, либо не использовать вообще.

Цитата:
Ведь после возникновения сеха, как правило, сделать уже почти ничего нельзя. Вернее, если продолжать работать, как ни в чем не бывало, то, скорее всего, будет еще хуже. Поэтому и смешивание исключений и сехов - ИМХО, уж очень нехорошо смотрится. Исключение - просто способ передачи управления, а win32-исключение - что-то почти всегда фатальное.

Я вообще-то ничего такого на замышлял. Улыбка

Тут, наверное, некоторые подумали, не принес ли я последние клетки головного мозга в жертву дешевому пиву и не собрался ли продолжать работу после отлова AV и того подобного.
Не, все путем, можно не волноваться Улыбка

Мне надо обрабатывать ошибки арифметики с плавающей точкой + целочисленные деления на ноль.

Выхода тут 3.

1. Ручная проверка допустимости параметров (0, <0, isnan, finite и т.п.)
2. Использование __try/__except
3. Использование _set_se_translator + трансляция SEH в cpp + try/catch для транслированных исключений cpp.

Везде, как водится, свои плюсы и минусы.
Короче, стою, и вспоминаю: налево пойдешь - xxx направо пойдешь - xxx прямо пойдешь - xxx, а слишком долго думать будешь - прямо здесь - xxx  Очень довольный
  

De quelle planète es-tu?
Наверх
 
IP записан
 
Uzhast
1c++ power user
Отсутствует



Сообщений: 1341
Зарегистрирован: 30. Августа 2006
Пол: Мужской
Re: Исключения: SEH и С++
Ответ #5 - 05. Ноября 2007 :: 21:34
Печать  
kms писал(а) 31. Октября 2007 :: 20:27:
В итоге, модуль, который загружается вторым, переопределяет se_translator на свой, и в первый модуль начинают прилетать не CDivideByZero, а CFckngDiv, что соответствующим образом влияет на общий анамнез.

Есть вероятность, что здесь есть какие-то тонкости, но на первый взгляд этот механизм, либо надо использовать единолично, когда больше никто не использует, либо использовать один se_translator на все модули, либо не использовать вообще.

Ну, _set_se_translator возвращает предыдущий транслятор. Поэтому при многомодульности есть более-менее нормальный способ: при входе в свой модуль запомнить старый транслятор, установить свой, поработать, установить старый транслятор и выйти. Ну, геморройно. А кому сейчас легко? Улыбка
  
Наверх
 
IP записан
 
Переключение на Главную Страницу Страницы: 1
ОтправитьПечать