How to throw good exceptions?
Asked Answered
R

12

13

I heard you should never throw a string because there is a lack of information and you'll catch exceptions you dont expect to catch. What are good practice for throwing exceptions? do you inherit a base exception class? Do you have many exceptions or few? do you do MyExceptionClass& or const MyExceptionClass& ? etc. Also i know exceptions should never been thrown in destructors

i'll add that i understand design by contract and when to throw exception. I am asking how i should throw exceptions.

Rudman answered 17/2, 2009 at 10:39 Comment(0)
N
13

In my opinion, a function should throw an exception if it can't keep its "promise", if it has to break its "contract". The function's signature (name and parameters) determine its contract.

Given these two member functions:

const Apple* FindApple(const wchar_t* name) const;
const Apple& GetApple(const wchar_t* name) const;

The names of these functions as well as their return values indicate to me that in the case of FindApple the function is perfectly capable of returning NULL when the correct apple was not found, but in the case of GetApple you're expecting an apple to return. If that second function can't keep its promise, it must throw an exception.

Exceptions are meant for those exceptional conditions in which a function has no other way of reporting these conditions. If you decide to make it a part of the promise (read: function signature) then it can report that condition without throwing an exception.

Note that in the case of FindApple, it's up to the caller to decide how to handle the condition of "not finding the right apple" because it's no longer an exceptional condition.

You might be tempted to try to avoid all exceptions, but that means you have to account for all possible exceptional conditions, and you're placing the burden on the caller instead. The caller needs to check for "error conditions" then.

Ultimately, an exception needs to be handled, but only by the caller that knows how to handle a particular condition in a useful way. And I mean this in the widest possible interpretation: a service that gives up will try again later, a UI that provides a helpful error message, a web app that presents a "oops" screen but that recovers nicely, ... and so on.

Dave

Nerin answered 17/2, 2009 at 11:22 Comment(2)
This is a very nice answer with respect to when/why to throw an exception. However, the question asks how.Beslobber
This question is also asking what to throw. On this, please see my answer to this question here.Beslobber
T
9

One basic thing is to reserve exceptions for exceptional situations only. Don't use them for flow control. For instance, "file not found" should not be an exception, it should be an error code or return value (unless the file is something that must exist, e.g. a configuration file). But if a file suddenly disappears while you're processing it, then throwing an exception is a good choice.

When exceptions are used sparingly, you don't need to turn your code into a try-catch -spaghetti in order to avoid receiving incomprehensible-in-the-context exceptions from the deeper layers.

Tarrel answered 17/2, 2009 at 10:47 Comment(5)
I would rephrase your example to make it a little less absolute. If the file you're looking for is a configuration file which must be present for a valid install of your product, then it is an exceptional situation that it's not found.Skywriting
Also, keep in mind that this rule is specific to languages featuring heavyweight (costly) exceptions such as C++.Frustum
No, I don't think runtime cost is a factor in the decision to use exceptions or not. No matter the cost, exceptions are handled outside the normal program flow. Cost is irrelevant.Nerin
@Richard: thanks for the rephrasing, clarified myself now. As for the cost, I don't either think it's relevant. If there are so many exceptions around that they cause performance problems, then the real problem is somewhere else than in the weight of the exceptions...Tarrel
I agree with Dave Van den Eynde. Excepcions are for excepcional situations, and that's nothing to do with cost.Thomey
F
4

Use the standard exceptions! If you have a specific error, try to avoid it with return value. If you have to use exceptions, define your custom exception that inherits from Exception and create a custom message.

Fungi answered 17/2, 2009 at 10:43 Comment(0)
D
3

Sometimes it can happen that you're not able to return error code eg. when you need exact context of when error situation occured, eg. when you need to propagate error status 3 levels up - you loose context.

In this situation custom class is the best solution. I use this approach, defining my own inline classes (there's no .cpp for them; only .h) eg.:

class DeviceException {
    ;
}

class DeviceIOException: public DeviceException {
    DeviceIOException(std::string msg, int errorCode);
}

etc.

I then can judge/act upon the exception by type and by information contained within.

Diadem answered 17/2, 2009 at 11:5 Comment(0)
A
1

I always throw an exception with a message of where it occurred and what caused it to happen:

throw NException("Foo::Bar", "Mungulator cause a stack overflow!");

You can then use these strings in messageboxes etc.

I always catch via

catch (NException& ex) { ... }

If you running windows you can pass the error value and have a function derive the error message. The best example of this is in Windows via C/C++ by Jeffrey Richter.

Audriaaudrie answered 17/2, 2009 at 11:19 Comment(2)
Is NException a derived class? (The question in part asks, "do you inherit a base exception class?")Beslobber
Yes, it is a derived class - I was big on deriving everything back then, but the product seemed to need it.Audriaaudrie
S
1

Throwing pointers is probably not a good thing, as it complicates ownership of the thrown object. Class type exceptions are probably better than fundamentals simply because they can contain more information about the reason for the exception.

In using a class or class hierarchy there are a couple of points you should consider:

  1. Both the copy constructor and destructor of the exception object must never throw an exception. If they do you're program will terminate immediately.(ISO 15.5/1)

  2. If your exception objects have base classes, then use public inheritance.
    A handler will only be selected for a derived to base class if the base class is accessible.(ISO 15.3/3)

  3. Finally, (for all exception types) ensure that the expression being thrown cannot itself result in an exception being thrown.

For example:

class Ex {
public:
  Ex(int i) 
  : m_i (i)
  {
    if (i > 10) {
      throw "Exception value out of range";
    }
  }

  int m_i;
};


void foo (bool b) {
  if (! b) {
     // 'b' is false this is bad - throw an exception
     throw Ex(20);    // Ooops - throw's a string, not an Ex
  }
}
Skywriting answered 17/2, 2009 at 11:29 Comment(0)
C
1

You should always throw an exception class derived from std::exception. This allows a certain consistency to your interface and allows more flexibility to the clients of these methods or functions. For example if you want to add a catch all handler you may be able to add a

catch(std::exception& e)
block and be done with it. (Though often you won't be able to get away with that if you don't control all the code that can throw).

I tend to throw only exceptions provided by the the standard (i.e. std::runtime_error) but if you want to provide extra granularity to your handlers, you should feel free to derive your own from std::exception. See the C++ FAQ lite.

Also, you should throw a temporary and catch it by reference (to avoid the copy ctor be invoked at your catch site). Throwing pointers is also frowned upon since it is unclear who should clean up the memory. C++ FAQ Lite deals with this too.

Conakry answered 18/2, 2009 at 0:38 Comment(1)
a lot of resources? a class without attributes and just a couple of virtual methods. If that is a lot of resouces probably exceptions are not for you.Varied
A
0

For a current project, we thought about the appropriate action that could be taken by the main program loop. The basic program accepts XML messages, and saves the information into a database (with a fair amount of processing in between).

  1. Data errors that indicate something wrong the input. Appropriate action is to save the message to a log directory but not process it.
  2. Infrastructure errors that indicate some subcomponent (like the input queue, an SQL database, a JNI library) is malfunctioning. Sleep for a few minutes then reconnect.
  3. Configuration errors that indicate some aspect configuration is unworkable. Exit the program.

The first item is a checked exception, since we considered data checking to be part of a method's interface. The others are unchecked since the main loop cannot know the implementations of subcomponents, e.g. an implementation may use an SQL database, or may simply persist data in memory -- the caller doesn't need to know.

Ardussi answered 17/2, 2009 at 10:52 Comment(0)
G
0

As it has been already said use them for exceptional situations only.

Always provide a way for the user to avoid throwing an exception, eg. if you have method, that will throw if something goes wrong like this:

public void DoSomethingWithFile() {
    if(!File.Exists(..))
        throw new FileNotFoundException();
}

Provide another method for the user to call:

public bool CanDoSomething() {
    return File.Exists(..);
}

This way there the caller can avoid exceptions if he wants. Do not hesitate to throw if something is wrong - "fail fast", but always provide exception-free path.

Also keep your exception class hierarchy flat and take a look at the standard exceptions like InvalidStateException and ArgumentNullExcpetion.

Gasper answered 17/2, 2009 at 11:38 Comment(4)
why should i supply a exception-free path? Why would a caller 'require' or really want an exception-free path?Rudman
Exceptions are for exceptional situations only. If a caller can't avoid them just to use your code you are making them part of the normal program flow, which can have a huge performance impact. Look at "Framework design guideliness" and msdn documentation.Gasper
A design like that presents a race condition, particularly in the example you gave. It's even worse if the existence check is instead a check of something like permissions. Better to try what you want to do, and have it succeed safely or fail as you attempt it.Tauto
:) The Point of this post is to demonstrate throwing and handling of exceptions. And it demonstrates it well. However it is not a production code and at the particular example it does contain race condition - I have never claimed it doesn't - congratulations you saw it!Gasper
G
0

If you're throwing exceptions from a component other developers will be using downstream, please do them a big favour and always derive your own exception classes (if you really need them c.f using the standard exceptions) from std::exception. Avoid at all costs utter abominations like throwing ints, HRESULTS, char*, std::string...

Glarum answered 17/2, 2009 at 13:21 Comment(0)
R
0

Here is a simple example of throwing an exception that takes barely any resources:

class DivisionError {};
class Division
{
public:
    float Divide(float x, float y) throw(DivisionError)
    {
        float result = 0;
        if(y != 0)
            result = x/y;
        else
            throw DivisionError();
        return result;
    }
};

int main()
{
    Division d;
    try
    {
        d.Divide(10,0);
    }
    catch(DivisionError)
    {
        /*...error handling...*/
    }
}

The empty class that is thrown does not take any resource or very few...

Reproach answered 4/7, 2009 at 3:45 Comment(0)
B
0

From the C++ FAQ, [17.12] What should I throw?:

Generally, it's best to throw objects, not built-ins. If possible, you should throw instances of classes that derive (ultimately) from the std::exception class.

...and

The most common practice is to throw a temporary: (see example that follows)

Beslobber answered 4/2, 2013 at 16:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.