The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

Where to put Exceptions?

I'm doing C++ andI  have a main() that reads a command line and performs a command.
The main calls a command line parser that returns a CAction. CAction is really an abstract class that has a pure virtual function called Execute().
CDoThis and CDoThat are concrete commands that derive from CAction. So they both have an Execute function that performs an action-specific set of operations.

main() only has to know about the CAction object, it's beautiful like in the books. So far, so good.

Here is a simplified version:

#include "CAction.h"
main( argc, argv )
{
    CAction *p;
    
    pAction = Parse( argc, argv );
    try
    {
        // call Execute for CDoThis or CDoThat
        pAction->Execute();
    }
    catch( ... )
    {
        cout << "something went wrong";
    }
    delete pAction;
}

Now the problem is, I am not satisfied with the catch(...). DoThis might throw exception EDoThis, and DoThat EDoThat, and that's what I want to catch.

So I would write:

    try
    {
        pAction->Execute();
    }
    catch( EDoThis& e )
    {
        cout << e.GetMsg();
    }
    catch( EDoThat& e )
    {
        cout << e.GetMsg();
    }
    catch( ... )
    {
        cout << "unknown error";
    }

But if I do that, now I have to give main() "inside knowledge" of EDoThis and EDoThat by including CDoThis.h and CDoThat.h (since the corresponding exceptions are defined in the same .h).

Is this the correct way to do things?
Or should I put the exceptions in a .h by themselves?
Or is it not a big deal at all and I'm worrying about a non-problem?

I have read Soustrup, and Effective C++, and havent found any recommendation regarding this. They all say that exceptions are cool but there's no advice on organizing them and fitting them cleanly in a project  architecture.
Any thoughts?

Thanks
Parisian developer ISO cute geek girl
Saturday, September 17, 2005
 
 
um that's "Stroustrup" !!! sorry...
Parisian developer ISO cute geek girl
Saturday, September 17, 2005
 
 
You could derive your exceptions from a common base class, and have the catch handler catch objects of that type. The rules for what an exception handler will catch are similar (I believe) to those for determining the function to call based on the arguments. So, an exception handler for objects of a particular class will also catch otherwise-uncaught objects of classes derived from that class.

(As with functions, of course, you should catch a reference to the object -- but strictly speaking, that's a separate issue :)

There's a suitable base class in the libs already -- std::exception. I haven't used it that much, but when I have used it it has been satisfactory. Includes a virtual function to retrieve a text message, too.
Tom_
Saturday, September 17, 2005
 
 
You could also make both CDoThis and CDoThat throw an EAction exception, but you could store information inside the EAction object regarding the actual source of or cause of the error before you throw it. You could then interrogate the EAction object when you later catch it, to find out where exactly it came from.
Ian Boys Send private email
Sunday, September 18, 2005
 
 
I agree with both responses so far.

To elaborate, at that point in the program the only useful thing you can do with an exception is report it. Derive all your exception classes from a base that provides an error message, and you only need to write one simple catch block.

The pedantic approach would be to derive from one of the subclasses of std::exception, such as std::runtime_error, but I don't see any advantage to doing so in this case.
comp.lang.c refugee
Sunday, September 18, 2005
 
 
If you could, if you would...

Idealy the function your calling should also declare what sort of exceptions it can throw, in overloading it, you should still be throwing the same set of exceptions.

So conceptualy - unless you wish to educate you Action about all the child classes exceptions you shouldn't throw entirely "different" exceptions from each class.

Now if the exceptions had the same parent that means they are no longer different - they could also have virtual functions to support specialisation.

Sunday, September 18, 2005
 
 
I'd say the exception should go around the "parse" part. That's where resources are allocated and illegal inputs are going to happen.

If the "parse" works out ok then the only exceptions should be due to bugs in your code, not the input data.
Joce
Sunday, September 18, 2005
 
 
I have used refugee & Ian's suggestion: the client sees an EAction. EDoThis and EDoThat are derived from that but not seen by the client. EDoThis and EDoThat take different parameters but they end up building an error message accessible from EAction. It looks clean enough.

Joce: Parse can throw (I omitted that for brevity).
But once I have a correct command line, processing throws also in case something goes wrong (file not found, cant make connection, other unexpected failures).

Thanks
Parisian developer ISO cute geek girl
Monday, September 19, 2005
 
 
I think it's a bad design. The different actions can throw very different exceptions, tied closely to the kind of action they perform, and they must all be handled in particular ways.

Ergo, the calling class needs to know about the actions it's executing, whether it is by catching specific exceptions or by digging out exception information from a general exception. In my opinion, using a general exception is just shortcircuiting the exception mechanism and loses you all the benefits of exceptions, for instance compile time checking that all exeption are handled.

Ask yourself if you really need inheritance for your actions. Is there a significant amount of shared behavior in the super class? It doesn't sound like it so why would you force inheritance?

Just parse the input and call a specific action in each case. The action does not have to extend anything and has its own throws clause with the exceptions relevant to what it does.
jz Send private email
Monday, September 19, 2005
 
 
I don't see the benefit of using different classes for EDoThis and EDoThat ... instead, why not just make all your code throw one type of exception?
Christopher Wells Send private email
Monday, September 19, 2005
 
 
What you're describing here is the Command pattern [http://c2.com/cgi/wiki?CommandPattern].

Keep exception handling abstract, at the pattern level. Deffer implementation to command specific exceptions.

class CommandException ()
class ACommandException : public CommandException {}
class BCommandException : public CommandException {}

ACommand() // throws ACommandException
BCommand() // throws BCommandException


} catch (CommandException& e) {
  handleCmdException(e);
} catch(...) {
  handleUnknownException();
}

If you need to do more than this, most likely you are throwing exceptions when you shouldn't.
Dino Send private email
Monday, September 19, 2005
 
 
Dino: that's exactly what my code now looks like. Thanks for the reference.

jz: the benefit is that my main() is able to call Execute for any action (the generic action represented by abstract class CAction) without modification or a huge switch statement like in the good old days of C. I actually think this abstract class stuff is pretty cool. Since we are talking patterns, I believe this is sort of like a Factory pattern (anyone care to confirm this?)

Christopher Wells: CDoThis can throw several different kinds of exceptions; I use derived exception classes because each exception takes different parameters and assembles an error message in it's constructor. The error message is a member of EAction.

This allows me to have only one place where the error message is assembled (in the exception ctor); otherwise, if I only throw EAction from everywhere, I'd have to sprinkle the message assembling in different places throughout the application.

If necessary, I can even add logging to a file in one of the derived exceptions.

Oh well. I hope I'm doing all this stuff right......
Parisian developer ISO cute geek girl
Monday, September 19, 2005
 
 
> the benefit is that my main() is able to call Execute for any action (the generic action represented by abstract class CAction) without modification or a huge switch statement like in the good old days of C.

Forcing actions to derive from a common base class restricts proper exception handling as you can no longer introduce new exceptions in individual actions. If the inheritance doesn't yield any other benefits than syntactical tricks then that is inappropriate use of inheritance.

I speak from the experience of working with command pattern frameworks which do exactly this, and developers suffer from the inability to force clients to deal with particular exceptional conditions, because our actions must all throw GeneralException or nothing. My experiences may or may not apply in your situation, you be the judge.
jz Send private email
Monday, September 19, 2005
 
 
Is it exceptional that a user would provide a file name that didin't exist?
You have already stated it, so why is it exceptional...

I would suggest that you should be checking for these class invariants on construction and if they fail you should be throwing from your parse function.


Once an exception is raised, what more do you realy need from the exception?  Should it be holding database cursors, file handles, one or many text messages - how would you actually make use of any of this information?

Monday, September 19, 2005
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz