50

If I generate an exception on my own, I can include any info into the exception: a number of code line and name of source file. Something like this:

throw std::exception("myFile.cpp:255");

But what's with unhandled exceptions or with exceptions that were not generated by me?

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Mar
  • 941
  • 1
  • 10
  • 18
  • 11
    Minor note: That's not standard-conforming code; std::exception does not have a ctor that takes a string. However MSVC does (incorrectly) allow it... – MattyT Dec 08 '08 at 23:09
  • 6
    Indeed. You should be throwing `runtime_error`; "exception" says nothing at all. – Lightness Races in Orbit Nov 09 '11 at 16:49
  • http://stackoverflow.com/questions/3026649/getting-information-about-where-c-exceptions-are-thrown-inside-of-catch-block – Lacobus Jul 19 '16 at 16:20

12 Answers12

47

A better solution is to use a custom class and a macro. :-)

#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;
    }
}
Frank Krueger
  • 69,552
  • 46
  • 163
  • 208
  • I don't like macros in general but this is good sample of good macro use. __FIE__, __LINE__ are very handy in finding bugs quickly. +1. – Nazgob Dec 08 '08 at 08:20
  • 4
    Recommended practice is to do anything that might allocate memory (such as the stringstream manipulation) in the what() function instead of the constructor. The reason is that there may be more resources available at the catch point (once the stack has unwound a bit) than at the throw point. – Steve Jessop Dec 08 '08 at 11:48
  • 2
    You may want to use `: std::runtime_error(std::string(file)+":"+std::to_string(line)+":"+arg)` to avoid the ostream manipulation. – Étienne Jul 16 '14 at 14:35
  • 5
    This will only work if you are working with your own code. Which the question states they are not. – Banjocat Feb 09 '15 at 19:52
41

It seems everyone is trying to improve your code to throw exceptions in your code, and no one is attempting the actual question you asked.

Which is because it can't be done. If the code that's throwing the exception is only presented in binary form (e.g. in a LIB or DLL file), then the line number is gone, and there's no way to connect the object to to a line in the source code.

James Curran
  • 101,701
  • 37
  • 181
  • 258
  • 3
    Is there a way to print address of the next instruction when exception was raised? If we can have instruction address we can easily get the line of code. – kanna May 11 '12 at 18:09
  • 2
    If you want stack traces and other 21st century features use Java. Or .Net. C++ is meant to be difficult. – Tuntable Aug 15 '18 at 05:49
  • 21
    @Tuntable: It wasn't "meant to be difficult". A better way to put it is "It wasn't built for comfort; it was built for speed". – James Curran Aug 15 '18 at 13:56
  • @JamesCurran then a very poor job was done because raw C is just a tad bit faster, with even less comfort. Clearly it wasn't the main goal. – Kaihaku Sep 17 '21 at 13:11
  • @Kaihaku Raw C is faster, sure, but in any language there's a tradeoff between development time and speed. That C++, when it comes to exceptions, went for speed is a valid tradeoff. And that's why James Curran's answer is both most correct one and also not necessarily the most helpful. – PixelArtDragon Jan 16 '22 at 10:35
  • If an object is thrown, C++ standard requires that it's copied to magical memory location (you can assume outside stack memory area), the stack unwinding will destroy all data in the stack during stack unwinding and once the exception is catched somewhere, the end of the catch block deletes the object in magical memory location. Not a single pointer or even an error code is stored unless specifically stored by the copy-constructor of the originally thrown exception. And if you throw a pointer to newly allocated object, nobody is going to automatically call delete/free on that pointer. – Mikko Rantalainen Jan 24 '23 at 08:54
28

There are several possibilities to find out where the exception was thrown:

Using compiler macros

Using __FILE__ and __LINE__ macros at throw location (as already shown by other commenters), either by using them in std exceptions as text, or as separate arguments to a custom exception:

Either use

throw std::runtime_error(msg " at " `__FILE__` ":" `__LINE__`);

or throw

class my_custom_exception {
  my_custom_exception(const char* msg, const char* file, unsigned int line)
...

Note that even when compiling for Unicode (in Visual Studio), FILE expands to a single-byte string. This works in debug and release. Unfortunately, source file names with code throwing exceptions are placed in the output executable.

Stack Walking

Find out exception location by walking the call stack.

  • On Linux with gcc the functions backtrace() and backtrace_symbols() can get infos about the current call stack. See the gcc documentation how to use them. The code must be compiled with -g, so that debug symbols are placed in the executable.

  • On Windows, you can walk the stack using the dbghelp library and its function StackWalk64. See Jochen Kalmbach's article on CodeProject for details. This works in debug and release, and you need to ship .pdb files for all modules you want infos about.

You can even combine the two solutions by collecting call stack info when a custom exception is thrown. The call stack can be stored in the exception, just like in .NET or Java. Note that collecting call stack on Win32 is very slow (my latest test showed about 6 collected call stacks per second). If your code throws many exceptions, this approach slows down your program considerably.

vividos
  • 6,468
  • 9
  • 43
  • 53
6

The simplest solution is to use a macro:

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

void f() {
    throw_line("Oh no!");
}
Frank Krueger
  • 69,552
  • 46
  • 163
  • 208
  • 4
    I thought the point of the question was that the exception was raised by code elsewhere, not under the control of the original poster? – Alastair Dec 08 '08 at 07:23
5

No one mentioned boost so far. If you're using boost c++ libraries they do come with some nice exception defaults for this:

#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;
}

And then you might get something like:

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

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

Dominic
  • 786
  • 6
  • 8
3

If you have a debug build and run it in the Visual Studio debugger, then you can break into the debugger when any kind of exception is thrown, before it propagates to the world.

Enable this with the Debug > Exceptions menu alternative and then check-marking the kinds of exceptions that you are interested in.

You can also add the ability to create a dump file, if the application source code is your own. With the dump file and PDB files (symbols) for the specific build, you'll get stacktraces with WinDbg, for example.

Johann Gerell
  • 24,991
  • 10
  • 72
  • 122
2

I think that a stack trace should get you to the point.

Marcin Gil
  • 68,043
  • 8
  • 59
  • 60
  • 1
    He'll only get a stacktrace if he can break into the debugger or if he has a dump file with accompanying symbols. – Johann Gerell Dec 08 '08 at 07:17
  • 1
    Being able to analyse dump files is very usefull. I think the poster should overcome his reluctancy to work with call stacks and crash dumps, get his hands dirty, and generate minidumps for post mortem analysis. – Suma Dec 08 '08 at 09:34
2

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__) )

```

aquirdturtle
  • 2,038
  • 26
  • 20
1

I found 2 solutions, but neither is fully satisfactory:

  1. If you call std::set_terminate, you can from there print the callstack right from the third party exception throw. Unfortunately, there's no way to recover from a terminate handler, and hence your application will die.

  2. If you call std::set_unexpected, then you need to declare as many as possible from your functions with throw(MyControlledException), so that when they throw due to third party called functions, your unexpected_handler will be able to give you a fine-grained idea of where your application threw.

Jason Plank
  • 2,336
  • 5
  • 31
  • 40
Daniel Pinyol
  • 2,094
  • 2
  • 15
  • 15
1

Compile your software in debug mode and run it with valgrind. It is mainly for finding memory leaks but it can also show you the exact line of where exceptions occur valgrind --leak-check=full /path/to/your/software.

Spixmaster
  • 143
  • 1
  • 3
  • 14
1

Others have already proposed using a macro and possibly a custom class. But in case you have an exception hierarchy, you need to also specify the exception type while throwing:

#define THROW(ExceptionType, message)                                    \
    throw ExceptionType(std::string(message) + " in " + __FILE__ + ':'   \
                        + std::to_string(__LINE__) + ':' + __func__)

THROW(Error, "An error occurred");

The assumption here is that all exceptions accept a single string argument, which is not limiting, as one can convert other arguments to strings (e.g. with std::to_string()) and concatenate them to a single string.

mrts
  • 16,697
  • 8
  • 89
  • 72
0

Apart from using a custom class with a macro, as suggested by Frank Krueger, for your own exceptions, you might be interested in taking a look at the structured exception handling mechanism (you're programming under windows, right?)
Check Structured Exception Handling on MSDN

Paolo Tedesco
  • 55,237
  • 33
  • 144
  • 193