Подтвердить что ты не робот

Вызов стека для исключений в С++

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

Это хорошая практика, или есть ли лучшие способы получить стек вызовов для исключения?

4b9b3361

Ответ 1

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

Сказав, что, если у вас действительно есть трассировка стека, нужно сделать так, чтобы информация о стеке вызовов ONCE на сайте исключения была удалена. Нет единого портативного способа сделать это, но использовать что-то вроде http://stacktrace.sourceforge.net/ в сочетании с подобной библиотекой для VС++ не должно быть слишком сложно.

Ответ 2

То, что вы делаете, не является хорошей практикой. Вот почему:

1. Это не нужно.
Если вы скомпилируете свой проект в режиме отладки, чтобы генерировать отладочную информацию, вы можете легко получить обратную трассировку для обработки исключений в отладчике, таком как GDB.

2. Это громоздко.
Это то, что вы должны помнить, чтобы добавить к каждой функции. Если вам не хватает функции, это может вызвать много путаницы, особенно если это была функция, вызвавшая исключение. И любой, кто смотрит на ваш код, должен будет понять, что вы делаете. Кроме того, я уверен, вы использовали что-то вроде __FUNC__ или __FUNCTION__ или __PRETTY_FUNCTION__, которые, к сожалению, являются нестандартными (стандартного способа в С++ не получить имя функции).

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

Хорошая практика
Хотя может быть хорошей практикой поймать и реконструировать каждую функцию для создания трассировки стека, хорошей практикой является прикрепить имя файла, номер строки и имя функции, с которой первоначально было отправлено исключение. Если вы используете boost:: exception с BOOST_THROW_EXCEPTION, вы получите это поведение бесплатно. Также полезно приложить объяснительную информацию к вашему исключению, которое поможет в отладке и обработке исключения. Тем не менее, все это должно происходить в то время, когда строится исключение; как только он будет построен, ему должно быть разрешено распространять его обработчик... вы не должны многократно ловить и реконструировать более чем необходимо. Если вам нужно поймать и реконструировать определенную функцию, чтобы добавить какую-то важную информацию, это прекрасно, но улавливать все исключения в каждой функции и в целях прикрепления уже имеющейся информации - это слишком много.

Ответ 3

Одним из решений, которое может быть более изящным, является создание макроса/класса Tracer. Поэтому в верхней части каждой функции вы пишете что-то вроде:

TRACE()

и макрос выглядит примерно так:

Tracer t(__FUNCTION__);

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

Примеры реализации включают такие вещи, как http://www.drdobbs.com/184405270, http://www.codeproject.com/KB/cpp/cmtrace.aspx и http://www.codeguru.com/cpp/v-s/debug/tracing/article.php/c4429. Также Linux работает следующим образом http://www.linuxjournal.com/article/6391 может сделать это более естественным образом, как описано в этом вопросе: Как сгенерировать stacktrace при сбое моего приложения gcc С++. ACE ACE_Stack_Trace тоже стоит посмотреть.

Несмотря на это, метод обработки исключений является грубым, негибким и дорогостоящим. Разработка классов/макросов выполняется намного быстрее и может быть скомпилирована для сборки релизов, если это необходимо.

Ответ 4

Ответ на все ваши проблемы - хороший отладчик, обычно http://www.gnu.org/software/gdb/ на linux или Visual Studio в Windows. Они могут давать вам трассировки стека по требованию в любой момент программы.

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

Ответ 5

Посмотрите на этот СО-вопрос. Это может быть близко к тому, что вы ищете. Это не кросс-платформенный, но ответ дает решения для gcc и Visual Studio.

Ответ 6

Исключение, которое не обрабатывается, остается за обработкой вызывающей функции. Это продолжается до тех пор, пока не будет обработано исключение. Это происходит при попытке/улавливании вызова функции. Другими словами, если вызывается функция, которая не находится в блоке try, исключение, которое происходит в этой функции, будет автоматически передано в стек вызовов. Итак, все, что вам нужно сделать, это поставить самую верхнюю функцию в блок try и обработать исключение "..." в блоке catch. Это исключение поймает все исключения. Таким образом, ваша самая лучшая функция будет выглядеть примерно так:

int main()
{
  try
  {
    top_most_func()
  }
  catch(...)
  {
    // handle all exceptions here
  }
}

Если вы хотите иметь определенные блоки кода для определенных исключений, вы также можете это сделать. Просто убедитесь, что они встречаются до блока блокировки исключений "...".

Ответ 8

Еще один проект поддержки трассировки стека: ex_diag. Макросов нет, кросс-платформенный присутствует, нет огромных потребностей в коде, инструмент быстрый, понятный и простой в использовании.

Здесь вам нужно только обернуть объекты, которые нужно отслеживать, и они будут отслеживаться, если возникает исключение.

Ответ 9

Связывание с библиотекой libcsdbg (см. fooobar.com/info/45123/... для исходного ответа) выглядит как самый чистый способ получения трассировки стека без изменения исходного кода или стороннего источника код (т.е. STL).

Это использует компилятор для сбора фактической коллекции стека, которая действительно хочет, чтобы вы делали.

Я не использовал его, и он был испорчен GPL, но он выглядит как правильная идея.