C++ 如何知道导致异常的确切代码行?
如果我自己生成一个异常,我可以在异常中包含任何信息:代码行的数量和源文件的名称。大概是这样的:C++ 如何知道导致异常的确切代码行?,c++,exception,C++,Exception,如果我自己生成一个异常,我可以在异常中包含任何信息:代码行的数量和源文件的名称。大概是这样的: throw std::exception("myFile.cpp:255"); 但是,对于未处理的异常或不是我生成的异常,又有什么关系呢?我认为堆栈跟踪应该能让您直截了当。如果您有一个调试生成并在Visual Studio调试器中运行它,那么您可以在抛出任何类型的异常时,在异常传播到世界之前,闯入调试器 使用Debug>Exceptions菜单选项启用此选项,然后选中标记您感兴趣的异常类型 如果应用
throw std::exception("myFile.cpp:255");
但是,对于未处理的异常或不是我生成的异常,又有什么关系呢?我认为堆栈跟踪应该能让您直截了当。如果您有一个调试生成并在Visual Studio调试器中运行它,那么您可以在抛出任何类型的异常时,在异常传播到世界之前,闯入调试器 使用Debug>Exceptions菜单选项启用此选项,然后选中标记您感兴趣的异常类型
如果应用程序源代码是您自己的,您还可以添加创建转储文件的功能。例如,使用特定构建的转储文件和PDB文件(符号),您将使用WinDbg获得stacktraces。最简单的解决方案是使用宏:
#define throw_line(msg) \
throw std::exception(msg " " __FILE__ ":" __LINE__)
void f() {
throw_line("Oh no!");
}
更好的解决方案是使用自定义类和宏。:-)
#包括
#包括
#包括
#包括
类my_异常:public std::runtime_错误{
std::字符串msg;
公众:
my_异常(常量std::string&arg,常量char*文件,int行):
std::运行时错误(arg){
std::ostringstream o;
o除了像Frank Krueger建议的那样,为您自己的异常使用带有宏的自定义类之外,您可能有兴趣了解结构化异常处理机制(您正在windows下编程,对吗?
检查有几种方法可以找出引发异常的位置:
使用编译器宏
在抛出位置(如其他注释者所示)使用\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu文件
和\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu行
宏,或者在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中)时,文件也会扩展为单字节字符串。
这在debug和release中有效。不幸的是,带有代码抛出异常的源文件名被放置在输出可执行文件中
堆叠行走
通过遍历调用堆栈找出异常位置
- 在使用gcc的Linux上,函数backtrace()和backtrace_symbols()可以获取有关当前调用堆栈的信息。请参阅如何使用它们。必须使用-g编译代码,以便在可执行文件中放置调试符号
- 在Windows上,您可以使用dbghelp库及其函数StackWalk64遍历堆栈。有关详细信息,请参阅Jochen Kalmbach关于CodeProject的文章。这在调试和发行版中有效,并且您需要为所有需要相关信息的模块提供.pdb文件
当抛出自定义异常时,您甚至可以通过收集调用堆栈信息来组合这两种解决方案。调用堆栈可以存储在异常中,就像在.NET或Java中一样。请注意,在Win32上收集调用堆栈非常慢(我最近的测试显示每秒大约收集6个调用堆栈)。如果您的代码抛出许多异常,这种方法会大大降低程序的速度。似乎每个人都在努力改进您的代码,以便在代码中抛出异常,而没有人试图回答您提出的实际问题
这是因为无法完成。如果引发异常的代码仅以二进制形式呈现(例如,在LIB或DLL文件中),则行号消失,并且无法将对象连接到源代码中的一行。我找到了两种解决方案,但都不完全令人满意:
如果调用std::set_terminate
,则可以从第三方异常抛出处打印调用堆栈。不幸的是,无法从终止处理程序恢复,因此应用程序将死亡
如果调用std::set_unexpected
,则需要使用throw(MyControlledException)从函数中声明尽可能多的值
,这样当它们由于第三方调用的函数而抛出时,您的意外\u处理程序将能够为您提供应用程序抛出位置的细粒度概念
受Frank Krueger的答案和的文档的启发,我意识到您可以将Frank的答案(我已经使用了一段时间)与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 ( );
}
以下是我的实现:
#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 << "\n@ 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__) )
#包括
#包括
#包括
类错误:public std::runtime\u错误
{
公众:
错误(常量std::string&arg,常量char*文件,int行):std::runtime\u错误(arg)
{
loc=std::字符串(文件)+“行”+std::to_字符串(行);
std::ostringstream out;
OUT 到目前为止还没有人提到Boost。如果你使用Boost C++库,那么它们确实会有一些很好的异常默认值:
#include <boost/exception/diagnostic_information.hpp>
#include <exception>
#include <iostream>
struct MyException : std::exception {};
int main()
{
try
{
BOOST_THROW_EXCEPTION(MyException());
}
catch (MyException &ex)
{
std::cerr << "Unexpected exception, diagnostic information follows:\n"
<< boost::current_exception_diagnostic_information();
}
return 0;
}
#包括
#包括
#包括
结构MyException:std::exception{};
int main()
{
尝试
{
BOOST_THROW_异常(MyException());
}
捕获(MyException和ex)
{
std::cerr在调试模式下编译您的软件,并使用valgrind运行。它主要用于查找内存泄漏,但也可以向您显示异常发生的确切位置valgrind--leak check=full/path/to/your/software
其他人已经建议使用宏,可能还建议使用自定义类。但如果您有异常,请使用在层次结构中,您还需要在引发时指定异常类型:
#定义抛出(例外类型,消息)\
在“+”文件“+”中抛出异常类型(std::string(message)+”:”\
+std::to_string(uuu LINE_uuu)+':'+uu func_uu)
抛出(错误,“发生错误”);
这里的假设是,所有异常都接受一个字符串参数,而不是
#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 << "\n@ 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__) )
#include <boost/exception/diagnostic_information.hpp>
#include <exception>
#include <iostream>
struct MyException : std::exception {};
int main()
{
try
{
BOOST_THROW_EXCEPTION(MyException());
}
catch (MyException &ex)
{
std::cerr << "Unexpected exception, diagnostic information follows:\n"
<< boost::current_exception_diagnostic_information();
}
return 0;
}
Unexpected exception, diagnostic information follows:
main.cpp(10): Throw in function int main()
Dynamic exception type: boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<MyException> >
std::exception::what: std::exception