Thursday, June 28, 2012

Why did my std::runtime_error turn into an unknown exception?!

Been debugging some C++ code. It started off with a program just dying. I narrowed that down to an uncaught exception. I (eventually) narrowed that down to my own code throwing a std::runtime_error. Then much head-scratching ensued, as I was catching it in the catch( ... ) block instead of the catch (std::exception& e) block. I was even more surprised when I could reproduce it in an example that is as minimal as you can get. There is a bug in this code. Give yourself 20 seconds to see if you can spot it before moving on:

#include <iostream>
#include <stdexcept>

int main(int,char**){
try{
    throw new std::runtime_error("Hello!");
}catch (std::runtime_error& e){
    std::cerr<<"Runtime Error:" << e.what() << "\n";
}catch (std::exception& e){
    std::cerr<<"Exception:" << e.what() << "\n";
}catch (...){
    std::cerr<<"Unknown Exception\n";
}

return 0;
}

.
.
.

I'll give you a clue: if, like me, you also spend a lot of time in PHP you will find spotting the bug a lot harder.

.
.
.

Yes, it's that new statement. In PHP you throw exceptions with the throw new ErrorClass. If you do that in C++ you are throwing a pointer. Pointers have to be caught as pointers, not as the class they point to. (And, to quote from item 13 in More Effective C++: "Furthermore, catch-by-pointer runs contrary to the conventions established by the language itself.") I knew that, and never intended to throw by pointer; I wish g++ gave a warning for it.

So, to answer the question at the top: my std::runtime_error did not turn into an unknown exception; my std::runtime_error* did !!

No comments: