如果我自己生成异常,我可以在异常中包含任何信息:源文件的许多代码行和名称。像这样:
throw std::exception("myFile.cpp:255");
但是未处理的异常或者我没有生成的异常是什么?
答案 0 :(得分:29)
更好的解决方案是使用自定义类和宏。 : - )
#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;
}
}
答案 1 :(得分:22)
似乎每个人都在尝试改进代码以在代码中抛出异常,并且没有人尝试您提出的实际问题。
这是因为无法完成。如果抛出异常的代码仅以二进制形式呈现(例如,在LIB或DLL文件中),则行号消失,并且无法将对象连接到源代码中的行。
答案 2 :(得分:20)
有几种可能性可以找出抛出异常的位置:
使用编译器宏
在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 documentation如何使用它们。必须使用-g编译代码,以便将调试符号放在可执行文件中。
在Windows上,您可以使用dbghelp库及其函数StackWalk64来遍历堆栈。有关详细信息,请参阅CodeProject上的Jochen Kalmbach的article。这适用于调试和发布,您需要为所需的所有模块发送.pdb文件。
当您抛出自定义异常时,您甚至可以通过收集调用堆栈信息来组合这两种解决方案。调用堆栈可以存储在异常中,就像在.NET或Java中一样。请注意,在Win32上收集调用堆栈非常慢(我的最新测试显示每秒大约有6个收集的调用堆栈)。如果您的代码抛出许多异常,这种方法会大大减慢您的程序。
答案 3 :(得分:3)
如果你有一个调试版本并在Visual Studio调试器中运行它,那么你可以在它传播到世界之前抛出任何类型的异常时进入调试器。
使用调试&gt;启用此功能例外菜单替代,然后检查标记您感兴趣的异常种类。
如果应用程序源代码是您自己的,您还可以添加创建转储文件的功能。使用特定构建的转储文件和PDB文件(符号),您将获得使用WinDbg的堆栈跟踪。
答案 4 :(得分:2)
最简单的解决方案是使用宏:
#define throw_line(msg) \
throw std::exception(msg " " __FILE__ ":" __LINE__)
void f() {
throw_line("Oh no!");
}
答案 5 :(得分:1)
我认为堆栈跟踪可以帮助您达到目的。
答案 6 :(得分:1)
我找到了2个解决方案,但都没有完全令人满意:
如果你致电std::set_terminate
,你可以从那里打印出第三方异常抛出的callstack。不幸的是,没有办法从终止处理程序恢复,因此你的应用程序将会死亡。
如果你打电话给std::set_unexpected
,那么你需要使用throw(MyControlledException)
从你的函数中尽可能多地声明,这样当他们因第三方调用函数而抛出时,你的{{1}能够给你一个关于你的应用程序投掷位置的细粒度的想法。
答案 7 :(得分:0)
除了使用带有宏的自定义类(如Frank Krueger所建议),对于您自己的异常,您可能有兴趣看一下结构化异常处理机制(您在windows下编程,对吗?)<登记/> 查看Structured Exception Handling on MSDN
答案 8 :(得分:0)
受Frank Krueger的答案和std::nested_exception文档的启发,我意识到您可以将我已经使用了一段时间的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 ( );
}
输出
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 << "\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__) )
```
答案 9 :(得分:0)
到目前为止,没有人提到助推器。如果您使用的是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;
}
然后您可能会得到类似的东西:
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
文档:https://www.boost.org/doc/libs/1_63_0/libs/exception/doc/diagnostic_information.html
答案 10 :(得分:0)
以调试模式编译软件,然后使用valgrind运行它。它主要用于查找内存泄漏,但也可以向您显示发生异常的确切行valgrind --leak-check=full /path/to/your/software
。
答案 11 :(得分:0)
其他人已经建议使用宏,也可能使用自定义类。但是,如果您具有异常层次结构,则还需要在抛出时指定异常类型:
#define THROW(ExceptionType, message) \
throw ExceptionType(std::string(message) + " in " + __FILE__ + ':' \
+ std::to_string(__LINE__) + ':' + __func__)
THROW(Error, "An error occurred");
这里的假设是所有异常都接受一个字符串参数。