如何知道导致异常的确切代码行?

Mar*_*Mar 34 c++ exception

如果我自己生成异常,我可以在异常中包含任何信息:源文件的许多代码行和名称.像这样的东西:

throw std::exception("myFile.cpp:255");
Run Code Online (Sandbox Code Playgroud)

但是未处理的异常或不是由我生成的异常是什么?

Fra*_*ger 31

更好的解决方案是使用自定义类和宏.:-)

#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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这仅在您使用自己的代码时才有效.哪个问题表明他们不是. (4认同)
  • 建议的做法是执行任何可能在what()函数中而不是构造函数中分配内存(例如字符串流操作)的操作.原因是在捕获点可能有更多可用资源(一旦堆栈已经解开),而不是在投掷点. (3认同)
  • 您可能希望使用`:std :: runtime_error(std :: string(file)+":"+ std :: to_string(line)+":"+ arg)`来避免ostream操作. (2认同)

Jam*_*ran 24

似乎每个人都在尝试改进代码以在代码中抛出异常,并且没有人尝试您提出的实际问题.

这是因为它无法完成.如果抛出异常的代码仅以二进制形式呈现(例如,在LIB或DLL文件中),则行号消失,并且无法将对象连接到源代码中的行.

  • @Tuntable:它并非"意味着难".更好的方式是"它不是为舒适而建造的;它是为速度而建造的". (7认同)
  • 有没有办法在异常提出时打印下一条指令的地址?如果我们可以有指令地址,我们可以轻松获得代码行. (3认同)
  • 如果您想要堆栈跟踪和其他 21 世纪功能,请使用 Java。或者.Net。C++ 注定是困难的。 (2认同)

viv*_*dos 21

有几种可能性可以找出抛出异常的位置:

使用编译器宏

在throw位置使用__FILE____LINE__宏(如其他评论者已经显示的那样),或者在std例外中将它们用作文本,或者作为自定义异常的单独参数:

要么使用

throw std::runtime_error(msg " at " `__FILE__` ":" `__LINE__`);
Run Code Online (Sandbox Code Playgroud)

或扔

class my_custom_exception {
  my_custom_exception(const char* msg, const char* file, unsigned int line)
...
Run Code Online (Sandbox Code Playgroud)

请注意,即使在编译Unicode(在Visual Studio中)时,FILE也会扩展为单字节字符串.这适用于调试和发布.不幸的是,带有抛出异常代码的源文件名放在输出可执行文件中.

堆栈行走

通过遍历调用堆栈找出异常位置.

  • 在使用gcc的Linux上,函数backtrace()和backtrace_symbols()可以获得有关当前调用堆栈的信息.请参阅gcc文档如何使用它们.必须使用-g编译代码,以便将调试符号放在可执行文件中.

  • 在Windows上,您可以使用dbghelp库及其函数StackWalk64来遍历堆栈.有关详细信息,请参阅Jochen Kalmbach 关于CodeProject 的文章.这适用于调试和发布,您需要为所需的所有模块发送.pdb文件.

您甚至可以通过在抛出自定义异常时收集调用堆栈信息来组合这两种解决方案.调用堆栈可以存储在异常中,就像在.NET或Java中一样.请注意,在Win32上收集调用堆栈非常慢(我的最新测试显示每秒大约有6个收集的调用堆栈).如果您的代码抛出许多异常,这种方法会大大减慢您的程序.


Fra*_*ger 6

最简单的解决方案是使用宏:

#define throw_line(msg) \
    throw std::exception(msg " " __FILE__ ":" __LINE__)

void f() {
    throw_line("Oh no!");
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为问题的重点是异常是由其他地方的代码引发的,而不是在原始发布者的控制下? (4认同)

Dom*_*nic 5

到目前为止,没有人提到提升。如果您使用 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;
}
Run Code Online (Sandbox Code Playgroud)

然后你可能会得到类似的信息:

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
Run Code Online (Sandbox Code Playgroud)

文档:https://www.boost.org/doc/libs/1_63_0/libs/exception/doc/diagnostic_information.html