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

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

Действительно ли это - хорошая практика или является там лучшими способами получить стек вызовов для исключения?

30
задан Igor Oks 11 July 2010 в 11:31
поделиться

6 ответов

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

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

24
ответ дан 27 November 2019 в 23:51
поделиться

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

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

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

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

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

22
ответ дан 27 November 2019 в 23:51
поделиться

Одно из решений, которое может быть более изящным, заключается в создании макроса/класса 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, могут делать это более нативно, как описано в этом вопросе Stack Overflow: How to generate a stacktrace when my gcc C++ app crashes. Также стоит обратить внимание на ACE_Stack_Trace от ACE.

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

7
ответ дан 27 November 2019 в 23:51
поделиться

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

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

2
ответ дан 27 November 2019 в 23:51
поделиться

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

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

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

-1
ответ дан 27 November 2019 в 23:51
поделиться

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

1
ответ дан 27 November 2019 в 23:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: