Как узнать точную строку кода, где было вызвано исключение?
Если я генерирую исключение самостоятельно, я могу включить в него любую информацию: номер строки кода и имя исходного файла. Что-то вроде этого:
throw std::exception("myFile.cpp:255");
Но что с необработанными исключениями или с исключениями, которые не были сгенерированы мной?
Ответы
Ответ 1
Кажется, каждый пытается улучшить ваш код, чтобы генерировать исключения в вашем коде, и никто не пытается задать интересующий вас вопрос.
Это потому, что это невозможно. Если код, генерирующий исключение, представлен только в двоичной форме (например, в LIB или DLL файле), тогда номер строки исчез, и нет способа подключить объект к строке в исходном коде.
Ответ 2
Лучшим решением является использование настраиваемого класса и макроса.: -)
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
class my_exception : public std::runtime_error {
std::string msg;
public:
my_exception(const std::string &arg, const char *file, int line) :
std::runtime_error(arg) {
std::ostringstream o;
o << file << ":" << line << ": " << arg;
msg = o.str();
}
~my_exception() throw() {}
const char *what() const throw() {
return msg.c_str();
}
};
#define throw_line(arg) throw my_exception(arg, __FILE__, __LINE__);
void f() {
throw_line("Oh no!");
}
int main() {
try {
f();
}
catch (const std::runtime_error &ex) {
std::cout << ex.what() << std::endl;
}
}
Ответ 3
Есть несколько возможностей узнать, где было выбрано исключение:
Использование макросов компилятора
Использование макросов __FILE__
и __LINE__
в местоположении throw (как уже показано другими комментаторами), либо используя их в исключениях std как текст, либо как отдельные аргументы для настраиваемого исключения:
Используйте
throw std::runtime_error(msg " at " `__FILE__` ":" `__LINE__`);
или бросить
class my_custom_exception {
my_custom_exception(const char* msg, const char* file, unsigned int line)
...
Обратите внимание, что даже при компиляции для Unicode (в Visual Studio) FILE расширяется до однобайтовой строки.
Это работает в отладке и выпуске. К сожалению, имена исходных файлов с исключениями для исключения кода помещаются в выходной исполняемый файл.
Прохождение стека
Узнайте местоположение исключения, пройдя стек вызовов.
Вы даже можете объединить два решения, собирая информацию стека вызовов, когда генерируется настраиваемое исключение. Стек вызовов можно хранить в исключении, как в .NET или Java. Обратите внимание, что сбор стека вызовов на Win32 очень медленный (мой последний тест показал около 6 собранных стеков вызовов в секунду). Если ваш код содержит множество исключений, этот подход значительно замедляет вашу программу.
Ответ 4
Если у вас есть отладочная сборка и запускается ее в отладчике Visual Studio, вы можете вломиться в отладчик, когда генерируется какое-либо исключение, прежде чем оно распространится в мир.
Включите это с помощью альтернативного меню Отладкa > Исключения, а затем отметьте отмеченные типы исключений, которые вас интересуют.
Вы также можете добавить возможность создания файла дампа, если исходный код приложения является вашим собственным. С файлом дампа и файлами (символами) PDB для конкретной сборки вы получите, например, стек стеков с помощью WinDbg.
Ответ 5
Самое простое решение - использовать макрос:
#define throw_line(msg) \
throw std::exception(msg " " __FILE__ ":" __LINE__)
void f() {
throw_line("Oh no!");
}
Ответ 6
Я думаю, что трассировка стека должна довести вас до точки.
Ответ 7
Я нашел 2 решения, но ни один из них не является полностью удовлетворительным:
-
Если вы вызываете std::set_terminate
, вы можете оттуда распечатать стоп-код справа от третьего исключения исключения. К сожалению, нет способа восстановить из обработчика terminate, и, следовательно, ваше приложение умрет.
-
Если вы вызываете std::set_unexpected
, вам нужно объявить как можно больше из ваших функций с помощью throw(MyControlledException)
, так что, когда они будут выбрасываться из-за функций, вызываемых третьей стороной, ваш unexpected_handler
сможет дать вам тонкую идею о том, куда ваше приложение бросило.
Ответ 8
Помимо использования специального класса с макросом, как это предложил Фрэнк Крюгер, для ваших собственных исключений, вам может быть интересно взглянуть на механизм структурированной обработки исключений (вы программируете под окнами, верно?) < ш > Проверьте Обработка структурированных исключений в MSDN
Ответ 9
Вдохновленный ответом Фрэнка Крюгера и документацией для std :: nested_exception, я понял, что вы можете объединить ответ Фрэнка, который я использовал некоторое время, с std :: nested_exception для создания полной трассировки стека ошибок с информацией о файле и строке, Например, с моей реализацией, работает
#include "Thrower.h"
#include <iostream>
// runs the sample function above and prints the caught exception
int main ( )
{
try {
// [Doing important stuff...]
try {
std::string s = "Hello, world!";
try {
int i = std::stoi ( s );
}
catch ( ... ) {
thrower ( "Failed to convert string \"" + s + "\" to an integer!" );
}
}
catch ( Error& e ) {
thrower ( "Failed to [Do important stuff]!" );
}
}
catch ( Error& e ) {
std::cout << Error::getErrorStack ( e );
}
std::cin.get ( );
}
выходы
ERROR: Failed to [Do important stuff]!
@ Location:c:\path\main.cpp; line 33
ERROR: Failed to convert string "Hello, world!" to an integer!
@ Location:c:\path\main.cpp; line 28
ERROR: invalid stoi argument
Вот моя реализация:
#include <sstream>
#include <stdexcept>
#include <regex>
class Error : public std::runtime_error
{
public:
Error ( const std::string &arg, const char *file, int line ) : std::runtime_error( arg )
{
loc = std::string ( file ) + "; line " + std::to_string ( line );
std::ostringstream out;
out << arg << "\[email protected] Location:" << loc;
msg = out.str( );
bareMsg = arg;
}
~Error( ) throw() {}
const char * what( ) const throw()
{
return msg.c_str( );
}
std::string whatBare( ) const throw()
{
return bareMsg;
}
std::string whatLoc ( ) const throw( )
{
return loc;
}
static std::string getErrorStack ( const std::exception& e, unsigned int level = 0)
{
std::string msg = "ERROR: " + std::string(e.what ( ));
std::regex r ( "\n" );
msg = std::regex_replace ( msg, r, "\n"+std::string ( level, ' ' ) );
std::string stackMsg = std::string ( level, ' ' ) + msg + "\n";
try
{
std::rethrow_if_nested ( e );
}
catch ( const std::exception& e )
{
stackMsg += getErrorStack ( e, level + 1 );
}
return stackMsg;
}
private:
std::string msg;
std::string bareMsg;
std::string loc;
};
// (Important modification here)
// the following gives any throw call file and line information.
// throw_with_nested makes it possible to chain thrower calls and get a full error stack traceback
#define thrower(arg) std::throw_with_nested( Error(arg, __FILE__, __LINE__) )
"""