Inspired by Frank Krueger's answer and the documentation for std::nested_exception, I realized that you can combine Frank's answer, which I've been using for a while, with std::nested_exception to create a full error stack trace with file & line info. For example with my implementation, running
#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 ( );
}
outputs
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
Here's my implementation:
#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__) )
```