can I get description of an exception caught by
catch(...)
block? something like .what() of std::exception.
There is one trick you might be able to use:
catch(...) {
handle_exception();
}
void handle_exception() {
try {
throw;
} catch (const std::exception &e) {
std::cout << e.what() << "\n";
} catch (const int i) {
std::cout << i << "\n";
} catch (const long l) {
std::cout << l << "\n";
} catch (const char *p) {
std::cout << p << "\n";
} catch (...) {
std::cout << "nope, sorry, I really have no clue what that is\n";
}
}
and so on, for as many different types as you think might be thrown. If you really know nothing about what might be thrown then even that second-to-last one is wrong, because somebody might throw a char* that doesn't point to a nul-terminated string.
It's generally a bad idea to throw anything that isn't a std::exception or derived class. The reason std::exception exists is to allow everybody to throw and catch objects that they can do something useful with. In a toy program where you just want to get out of there and can't even be bothered to include a standard header, OK, maybe throw an int or a string literal. I don't think I'd make that part of a formal interface. Any exceptions you throw are part of your formal interface, even if you somehow forgot to document them.
That block might catch an int, or a const char*, or anything. How can the compiler possibly know how to describe something when it knows nothing about it? If you want to get information off an exception, you must know the type.
If you know you only throw std::exception or subclasses, try
catch(std::exception& e) {...e.what()... }
Otherwise, as DeadMG wrote, since you can throw (almost) everything, you cannot assume anything about what you caught.
Normally catch(...) should only be used as the last defense when using badly written or documented external libraries. So you would use an hierarchy
catch(my::specialException& e) {
// I know what happened and can handle it
... handle special case
}
catch(my::otherSpecialException& e) {
// I know what happened and can handle it
... handle other special case
}
catch(std::exception& e) {
//I can at least do something with it
logger.out(e.what());
}
catch(...) {
// something happened that should not have
logger.out("oops");
}
Since C++11 you can capture the current exception with a pointer:
std::exception_ptr p; // default initialization is to nullptr
try {
throw 7;
}
catch(...)
{
p = std::current_exception();
}
This behaves like a smart pointer; so long as there is at least one pointer pointing to the exception object it is not destroyed.
Later (maybe even in a different function) you can take action in a similar way to the current top answer:
try {
if ( p )
std::rethrow_exception(p);
}
catch(int x)
{
}
catch(std::exception &y)
{
}
How we have our exceptions implemented is that, we have our own Exception classes, that are all derived from std::exception..
Our exceptions will contain Exception message, Function name, File name and line where exceptions are generated. These are all useful not just to show the Messages but also can be used for logging which helps to diagnose the Exception quite easily. So, we get the entire information about the Exceptions generated.
Remember exceptions are for us to get information about what went wrong. So, every bit of information helps in this regard..
Quoting bobah
#include <iostream>
#include <exception>
#include <typeinfo>
#include <stdexcept>
int main()
{
try {
throw ...; // throw something
}
catch(...)
{
std::exception_ptr p = std::current_exception();
std::clog <<(p ? p.__cxa_exception_type()->name() : "null") << std::endl;
}
return 1;
}
Related
I am writing a program in C++ which handles errors via throwing strings (either std::string or const char*), like so :
if (/* failure condition 1 */)
throw std::string("Error 1 : ...") + std::to_string(foo);
if (/* failure condition 2 */)
throw "Error 2 : ...";
Then I would catch them this way :
try {
thisIsLikelyToThrowErrors();
} catch (const std::string& e) {
std::cerr << ":( " << e << std::endl;
} catch (const char* e) {
std::cerr << ":( " << e << std::endl;
}
I would like to avoid writing the same line twice, by forwarding the string from the first catch block to the second one :
try {
thisIsLikelyToThrowErrors();
} catch (const std::string& e) {
throw e.c_str(); // Does not work
} catch (const char* e) {
std::cerr << ":( " << e << std::endl;
}
However this code won't work, the thrown char const* simply won't be catched.
Is there a way to do this?
Thanks :)
No, you cannot do this.
[C++14: 15.1/1]: When an exception is thrown, control is transferred to the nearest handler with a matching type (15.3); “nearest” means the handler for which the compound-statement or ctor-initializer following the try keyword was most recently entered by the thread of control and not yet exited.
You would have to nest entire try/catch pairs:
try {
try {
thisIsLikelyToThrowErrors();
}
catch (const std::string& e) {
throw e.c_str(); // Does not work
}
}
catch (const char* e) {
std::cerr << ":( " << e << std::endl;
}
Ew!
But now you have another problem, in that the std::string e dies after you've thrown its C-string buffer (it'd stay alive if you re-threw it). Probably try to avoid this in general, eh?
Throw actual exceptions, instead.
You can handle the common setup in an independent function externally, calling it from your catches clauses.
In your example, you can do:
inline void HandleStringyException(const char* excStr)
{
//do something
}
Then:
try {
thisIsLikelyToThrowErrors();
} catch (const std::string& e) {
HandleStringyException(e.c_str());
} catch (const char* e) {
HandleStringyException(e);
}
P.S. It should be mentioned that exceptions are better to be handled polymorphically, and in order to do so, the objects thrown should be chosen wisely. As mentioned by others, throwing objects like strings and pointers is a bad practice, and generally you'd like to throw objects derived from std::exception. Sometimes, however, the throwing code is not under your control and you have to make the best of it. I think that's a different discussion.
There is a simple solution to your actual problem:
Throw only std::string or only const char*.
Or even better throw something derived from std::exception
Can I put just one all-encompassing try-catch statement in my main function that covers the entire program? Or do all functions require their own? What I mean is, will something like this work:
int main(){
try{
foo();
bar();
};
catch(char* e){
//Do stuff with e
};
};
void foo(){throw "You'll never reach the bar.";};
void bar(){throw "Told you so.";};
If not, is there a similar way this can be done?
Your example won't work because
Declaration of foo() and bar() are not before using them.
There is an extra semicolon between the block after try and catch.
What is passed to throw is const char*, but you catched only char*.
This example worked.
#include <iostream>
void foo();
void bar();
int main(){
try{
foo();
bar();
}
catch(const char* e){
//Do stuff with e
std::cout << e << std::endl;
}
}
void foo(){throw "You'll never reach the bar.";}
void bar(){throw "Told you so.";}
Can I put just one all-encompassing try-catch statement in my main
function that covers the entire program?
Yes. catch (...) catches everything.
#include <iostream>
int main()
{
try
{
// do something
}
catch (...)
{
std::cerr << "exception caught\n";
}
}
Or do all functions require their own?
No. That would defeat the whole purpose of exceptions.
catch(char* e){
//Do stuff with e
};
This code is a result of the misunderstanding that exceptions are error messages. Exceptions are not error messages. Exceptions in C++ can be of any type. This includes char*, of course, but it is completely unidiomatic.
What you really want to do is catch std::exception, which includes an error message, accessible via the what() member function. Well-written C++ code only throws exceptions of type std::exception or derived classes. You can add ... as a fallback for all other cases:
#include <iostream>
#include <exception>
int main()
{
try
{
// do something
}
catch (std::exception const& exc)
{
std::cerr << exc.what() << "\n";
}
catch (...)
{
std::cerr << "unknown exception caught\n";
}
}
throw "You'll never reach the bar.";
Consequently, throwing char arrays is wrong. It's wrong on a technical level if you expect a char const[] to be converted to a char*, but it's especially wrong on a design level. Replace the array with a dedicated exception type like std::runtime_error:
throw std::runtime_error("You'll never reach the bar.");
I have a std::exception_ptr with an exception inside it. I am going to invoke std::rethrow_exception to get the actual exception, will the exception be valid after the catch statement? My guess here is that since I still hold the std::exception_ptr it will still be valid.
See the example:
std::exception_ptr ePtr = initialize_somewhere_else();
std::runtime_error* a=NULL;
try {
std::rethrow_exception(ePtr);
} catch(std::runtime_error& e) {
a = &e;
}
std::cout << a << ", " << a->what() << std::endl;
Note: In my tests using Clang this does work.
Is it safe to use an exception outside the catch statement if it is held in a std::exception_ptr?
Yes. You can think of exception_ptr as a reference-counted pointer to the exception. The exception will live for as long as an exception_ptr refers to it, and no longer.
Additionally, rethrow_exception does not make a copy.
Sorry, I think I've given an incorrect answer.
Here is a detection test:
#include <iostream>
#include <exception>
#include <stdexcept>
int
main()
{
auto ePtr = std::make_exception_ptr(std::runtime_error("some text"));
std::runtime_error* a = nullptr;
try
{
std::rethrow_exception(ePtr);
}
catch(std::runtime_error& e)
{
a = &e;
}
*a = std::runtime_error("other text");
try
{
std::rethrow_exception(ePtr);
}
catch(const std::runtime_error& e)
{
std::cout << e.what() << '\n';
}
}
On gcc and clang this outputs:
other text
This indicates that a and ePtr refer to the exact same object.
However VS, current version according to http://webcompiler.cloudapp.net outputs:
some text
This indicates that a and ePtr refer to different objects.
The question now is whether VS is conforming in this regard. My opinion is that it doesn't really matter. This is a contentious area of the standard, and if contested, the standard will be changed to match existing practice.
It appears to me that rethrow_exception makes a copy on VS, and that implies that the lifetime of e in the original question is not guaranteed to be tied to ePtr.
When I use boost::copy_exception to copy an exception to an exception_ptr, I lose type information. Take a look at the following code:
try {
throw std::runtime_error("something");
} catch (exception& e) {
ptr = boost::copy_exception(e);
}
if (ptr) {
try {
boost::rethrow_exception(ptr);
} catch (std::exception& e) {
cout << e.what() << endl;
cout << boost::diagnostic_information(e) << endl;
}
}
From this, I get the following output:
N5boost16exception_detail10clone_implISt9exceptionEE
Dynamic exception type: boost::exception_detail::clone_impl<std::exception>
std::exception::what: N5boost16exception_detail10clone_implISt9exceptionEE
So basically boost::copy_exception statically copied the argument it got.
This problem is solved if I throw my exception with boost::enable_current_exception instead, like this.
try {
throw boost::enable_current_exception(std::runtime_error("something"));
} catch (...) {
ptr = boost::current_exception();
}
if (ptr) {
try {
boost::rethrow_exception(ptr);
} catch (std::exception& e) {
cout << e.what() << endl;
cout << boost::diagnostic_information(e) << endl;
}
}
The problem with this is that sometimes the exceptions are thrown by a library that does not use boost::enable_current_exception. In this case, is there any way to put the exception into an exception_ptr apart from catching each kind of possible exception one by one and use boost::copy_exception on each one?
This is by design, and your analysis is correct: the static type is used, not the dynamic type. In fact, to avoid this surprise, boost::copy_exception became std::make_exception_ptr during the process that led to C++11. That way, it's clearer that current_exception (whether the Boost version or the C++11 one) is the correct thing to use to correctly capture, well, the current exception. I highly recommend using BOOST_THROW_EXCEPTION, or at least boost::throw_exception, when it comes to using Boost.Exception-enabled exceptions in your code.
When it comes to third-party code, there is no solution other than the one you already mentioned, or some other moral equivalent (like dynamic_cast-ing to the different leaf classes that make up the exception type hierarchy or hierarchies, or typeid abuse).
In this respect exception handling is the same as working with one or more hierarchy of polymorphic types, and the operation you're attempting in this case is a virtual copy, also known as cloning: either your code is intrusively designed to support that (as is the case when using e.g. BOOST_THROW_EXCEPTION(e); instead of throw e;), or you're going to painfully walk the inheritance tree. Note that you can at least refactor that pain into just one site, such that you'd end up with e.g.
try {
third_party_api();
} catch(...) {
ptr = catch_exceptions_from_api();
}
Is there any way to get at least some information inside of here?
...
catch(...)
{
std::cerr << "Unhandled exception" << std::endl;
}
I have this as a last resort around all my code. Would it be better to let it crash, because then I at least could get a crash report?
No, there isn't any way. Try making all your exception classes derive from one single class, like std::exception, and then catch that one.
You could rethrow in a nested try, though, in an attempt to figure out the type. But then you could aswell use a previous catch clause (and ... only as fall-back).
You can do this using gdb or another debugger. Tell the debugger to stop when any exception is throw (in gdb the command is hilariously catch throw). Then you will see not only the type of the exception, but where exactly it is coming from.
Another idea is to comment out the catch (...) and let your runtime terminate your application and hopefully tell you more about the exception.
Once you figure out what the exception is, you should try to replace or augment it with something that does derive from std::exception. Having to catch (...) at all is not great.
If you use GCC or Clang you can also try __cxa_current_exception_type()->name() to get the name of the current exception type.
Yes there is, but how useful it is is open to debate:
#include <exception>
#include <iostream>
using namespace std;
int f() {
throw "message";
}
int main() {
try {
f();
}
catch ( ... ) {
try {
throw;
}
catch( const char * s ) {
cout << "caught " << s << endl;
}
}
}
And to actually to answer your question, IMHO you should always have a catch(...) at
the top level of your code, that terminates (or otherwise handles) when presented with an unexpected exception in your application, in a manner fully documented by your application's manual.
I believe you should catch (...), if you have a reasonable course of action at that point and want the application to keep running.
You don't have to crash in order to generate a crash report, mind you. There's API for generating a mini-dump and you can do it in your SEH handler.
here's an approach I used on one project. it involves rethrowing until an exception type is matched against a list of known exceptions and then dispatching some action upon a match (in this case just returning some string information, but it could also be calling a registered function object).
This idea can be extended into a dynamic registry of exception types if you wish, the thing you have to be careful of is to ensure that the list is in most-derived to least-derived order (requires a lot of rethrowing and catching during registration!)
#include <iostream>
#include <stdexcept>
#include <exception>
#include <typeinfo>
#include <system_error>
namespace detail {
// a function which compares the current exception against a list of exception types terminated
// with a void type
// if a match is made, return the exception (mangled) class name and the what() string.
// note that base classes will be caught if the actual class is not mentioned in the list
// and the list must be in the order of most-derived to least derived
//
template<class E, class...Rest>
std::string catcher_impl()
{
try
{
std::rethrow_exception(std::current_exception());
}
catch(const E& e)
{
bool is_exact = typeid(E) == typeid(e);
return std::string(typeid(E).name()) + (is_exact ? "(exact)" : "(base class)") + " : " + e.what();
}
catch(...)
{
return catcher_impl<Rest...>();
}
return "unknown";
}
// specialise for end of list condition
template<> std::string catcher_impl<void>()
{
return "unknown exception";
}
}
// catcher interface
template<class...Es>
std::string catcher()
{
return detail::catcher_impl<Es..., void>();
}
// throw some exception type
// and then attempt to identify it using the type list available
//
template<class E>
void test(E&& ex)
{
try
{
throw std::forward<E>(ex);
}
catch(...)
{
std::cout << "exception is: "
<< catcher<std::invalid_argument, std::system_error, std::runtime_error, std::logic_error>()
<< std::endl;
}
}
int main()
{
test(std::runtime_error("hello world"));
test(std::logic_error("my logic error"));
test(std::system_error(std::make_error_code(std::errc::filename_too_long)));
test(std::invalid_argument("i don't like arguments"));
struct my_runtime_error : std::runtime_error
{
using std::runtime_error::runtime_error;
};
test(my_runtime_error("an unlisted error"));
}
example output:
exception is: St13runtime_error(exact) : hello world
exception is: St11logic_error(exact) : my logic error
exception is: NSt3__112system_errorE(exact) : File name too long
exception is: St16invalid_argument(exact) : i don't like arguments
exception is: St13runtime_error(base class) : an unlisted error