如果我自己生成异常,我可以在异常中包含任何信息:源文件的许多代码行和名称.像这样的东西:
throw std::exception("myFile.cpp:255");
但是未处理的异常或不是由我生成的异常是什么?
更好的解决方案是使用自定义类和宏.:-)
#include#include #include #include 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; } }
似乎每个人都在尝试改进代码以在代码中抛出异常,并且没有人尝试您提出的实际问题.
这是因为它无法完成.如果抛出异常的代码仅以二进制形式呈现(例如,在LIB或DLL文件中),则行号消失,并且无法将对象连接到源代码中的行.
有几种可能性可以找出抛出异常的位置:
使用编译器宏
在throw位置使用__FILE__
和__LINE__
宏(如其他评论者已经显示的那样),或者在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也会扩展为单字节字符串.这适用于调试和发布.不幸的是,带有抛出异常代码的源文件名放在输出可执行文件中.
堆栈行走
通过遍历调用堆栈找出异常位置.
在使用gcc的Linux上,函数backtrace()和backtrace_symbols()可以获得有关当前调用堆栈的信息.请参阅gcc文档如何使用它们.必须使用-g编译代码,以便将调试符号放在可执行文件中.
在Windows上,您可以使用dbghelp库及其函数StackWalk64来遍历堆栈.有关详细信息,请参阅Jochen Kalmbach 关于CodeProject 的文章.这适用于调试和发布,您需要为所需的所有模块发送.pdb文件.
您甚至可以通过在抛出自定义异常时收集调用堆栈信息来组合这两种解决方案.调用堆栈可以存储在异常中,就像在.NET或Java中一样.请注意,在Win32上收集调用堆栈非常慢(我的最新测试显示每秒大约有6个收集的调用堆栈).如果您的代码抛出许多异常,这种方法会大大减慢您的程序.