views:

603

answers:

6

In my project we have a base exception. For handling showing error dialogs, log and such. Im looking for a way to handle all derived classes of that exception, I thought this would work:

try
{
  main_loop();
}
catch (const MyExceptionBase* e)
{
  handle_error(e);
}

As every child instance thrown could be represented by a pointer to its parent. But no, when exceptions are thrown now, its an unhandled exception.

Why is this? Do c++ only throw exceptions as references? Thereby rendering my catch block useless? But then why does this even compile in the first place?

The only other way I can think of is this:

try
{
  main_loop();
}
catch (const ExceptionA& e)
{
  handle_error(e);
}
catch (const ExceptionB& e)
{
  handle_error(e);
}
catch (const ExceptionC& e)
{
  handle_error(e);
}

Which seems kinda ugly. What is the correct way to do this? Dont have a base exception class? Or can it be solved in the way I want?

Ps: What handle_error() does is simply make use of the base class function display_message_box() and cleanly shutdown the program.

+1  A: 

This should work:

try {
  main_loop();
} catch (const MyExceptionBase &e) {
  handle_error(e);
}

I assume ExceptionA/B/C all inherit from MyExceptionBase...I think that should work just fine.

PS: you may want to consider having MyExceptionBase inhert from std::exception as well.

Evan Teran
Is there any extra benefit from inheriting from std::exception?
mizipzor
not a huge amount, maybe that an uncaught exception can have a more meaningful message (since many terminate handlers output the what() member).
Evan Teran
+9  A: 

Just mix the two approaches: use the base class, and use a reference.

try
{
  main_loop();
}
catch (const MyExceptionBase& e)
{
  handle_error(e);
}

BTW C++ can catch pointers, if you throw them. It's not advisable though.

jpalecek
I didnt know references could refer to derived class instances as well, ill try that out, thanks! :)
mizipzor
+7  A: 

Your best bet is to catch the base reference. But please do so by reference, not by pointer. Example

try
{
  main_loop();
}
catch (const MyExceptionBase& e)
{
  handle_error(e);
}

The problem with catching an exception by pointer is that it must be thrown by pointer. This means that it will be created with new.

throw new ExceptionA();

This leaves a rather large problem because it must be deleted at some point or you have a memory leak. Who should be responsible for deleting this exception? It's generally difficult to get this right which is why most people catch by reference.

In general in C++ you should ...

Catch by reference, throw by value

JaredPar
+1 for catch by reference throw by value. Who said that - Sutter?
Greg Rogers
@Greg, I know Sutter said it in his books but I'm not sure if he is the original source.
JaredPar
+1  A: 

I am surprised that your original example doesn't work. The following does work (at least with g++):

class Base { public: virtual ~Base () {} };
class Derived : public Base {};

int main ()
{
  try
  {
    throw new Derived ();
  }
  catch (Base const * b)
  {
    delete b;
  }
}

I'm also pretty sure that this is intended to work as per a bullet under 15.3/3:

the handler is of type cv1 T* cv2 and E is a pointer type that can be converted to the type of the handler by either or both of

Are you inheriting from the base exception type via public inheritance? The base class needs to be an accessible base and this would stop your exception from being caught.

As per all the other answers here, throwing/catching by reference has an advantage in that you do not need to worry about ownership of the memory etc. But if the example above doesn't work - then I don't know why the reference example would work.

Richard Corden
Yes, that does work. Reason why my code didnt was that I throw the exceptions with "throw ExceptionA;", no "new", so there never was a pointer. But as jpalecek stated, references to a parent can obviously point to a child as well, I didnt know about that but it seems to work now.
mizipzor
One point of caution, make sure the destructor and copy constructor for your exception hierarchy will definitely not throw an exception. Your object may be copied to the "temporary exception object" and that copy should not fail. Same goes for the destructor.
Richard Corden
@mizipzor: I suspected that was the problem. If you throw an object, you need to either catch it with an object (but don't do that) or an object reference, you can't catch it with a pointer-to-object. With "catch const MyExceptionBase* e)" you can only catch pointers (but don't do that either).
j_random_hacker
+1 Richard Corden. I knew about the need for non-throwing destructors, but not about the need for non-throwing copy ctors.
j_random_hacker
A: 

As others have mentioned, you should catch a reference to the base class exception.

As for why it compiles in the first place, unlike Java, there are no restrictions on what types can be thrown or caught. So you can put in a catch block for any type, even if it's never thrown, and your compiler will happily compile it. Since you can throw by pointer, you can also catch by pointer.

JohnMcG
+2  A: 

You can only use catch( const sometype* ptr ) if you are throwing pointers, which is inadvisable under 99% of circumstances.

The reason catch( const sometype& ptr ) works is because r-values are implicitly convertible to constant references.

Tanveer Badar