Sign in to follow this  
jwein

Using Exceptions or Return Values

Recommended Posts

jwein    100
Hello, I am rewriting some classes of my little framework and I would like to ask you which one of the following ways of managing errors and exceptions you think is more appropriate:
D3DEffect.cpp:
bool D3DEffect::Initialize(...)
{
    // Do some initializing stuff

    if(error initializing the effect)
        return false;

    return true;
}

Application.cpp:
int main()
{
    RenderSystem renderSystem;
    Effect* myEffect = renderSystem->CreateEffect();
    OtherClass otherClass;

    bool returnValue = true;

    returnValue = renderSystem.Initialize(...);
    returnValue = myEffect.Initialize(...);
    returnValue = otherClass.Initialize(...);

    if(returnValue == false)
    {
        ShowCriticalMessage("Could not initialize the engine!");
        TerminateApplication();
    }

    return 0;
}
Or...
D3DEffect.cpp:
void D3DEffect::Initialize(...)
{
    // Do some initializing stuff

    if(error initializing the effect)
        throw std::runtime_error("Could not initialize the effect!");
}

Application.cpp:
int main()
{
    RenderSystem renderSystem;
    Effect* myEffect = renderSystem->CreateEffect();
    OtherClass otherClass;

    try
    {
        renderSystem.Initialize(...);
        myEffect.Initialize(...);
        otherClass.Initialize(...);
    }
    
    catch(std::exception& e)
    {
        ShowCriticalMessage(e.what());
        TerminateApplication();
    }

    return 0;
}
Should I use return values or exceptions for errors like these? I've already made a search for an answer in other threads or websites but I couldn't understand how to manage errors in functions like the initializiation one I've presented here... I hope you'll help me :) Thank you in advance. [Edited by - jwein on March 15, 2010 11:01:03 AM]

Share this post


Link to post
Share on other sites
jpetrie    13103
In general, use both, as appropriate.

Quote:

bool D3DEffect::Initialize(...)

See, I would eschew this method entirely in favor of a constructor, and thus exceptions would be the only way to report an error. Using manual initialize/cleanup methods makes it more difficult to take advantage of RAII, one of the best features offered by C++.

Quote:

Should I use return values or exceptions for errors like these?

Only your second example, using exceptions, works reasonably well. The first example only checks the return value of the last initialize call. Both examples have some pretty glaring faults but they are not directly related to the actual checking of the error condition, aside from the aforementioned issue, so I will ignore them.

In this case I would prefer the exception method anyhow.

There are no firm rules, but there are guidelines you can follow. Generally, you should prefer exceptions for things that are quite unexpected or out of the ordinary -- things that should not happen during nominal execution. Failure to initialize a complete subsystem is rather abnormal, and thus exceptions should be used.

In contrast, some other things shouldn't use exceptions. Hit testing, for example, is expected to "fail" often during execution and so shouldn't use an exception to report that failure. Similarly, loading a texture from a delay-load cache may "fail" in a fashion that isn't critical, returning a dummy texture until the real texture gets pulled in (for example).

Another way to look at the problem is whether or not the failure would be one the caller of the function can reasonably be expected to handle. This generally involves looking at the problem from a broader perspective, of course.

Share this post


Link to post
Share on other sites
It mostly depends. Either is acceptable, but with exceptions, you have much more flexibility. On the other hand, exceptions are rather slow.

As a rule of thumb, use return values for non-fatal errors and exceptions for fatal errors or those requiring a good deal of information.

That being said, exceptions are being pushed more in C++. If you want to be trendy and not be yelled at for bad style by your Computer Science teacher :P, use exceptions.

Cheers,
Patrick

Share this post


Link to post
Share on other sites
jwein    100
Thank you for your answers. Now I understand the difference between return values and exceptions...

I would like to ask you another related question: If I have, for example, a class which represents a mathematical vector and I would like to check, in a function or in an operator, if a member or a parameter is correct (for example in a division if it's not equal to zero), should I use assert or an "if" condition to eventually call an abort function or throw an exception?

I know that asserts are not compiled in release mode, but the functions of mathematical classes are time critical and I don't know if using "if" conditions at runtime would be a good solution.

Share this post


Link to post
Share on other sites
jpetrie    13103
Quote:

Thank you for your answers. Now I understand the difference between return values and exceptions...

I would like to ask you another related question: If I have, for example, a class which represents a mathematical vector and I would like to check, in a function or in an operator, if a member or a parameter is correct (for example in a division if it's not equal to zero), should I use assert or an "if" condition to eventually call an abort function or throw an exception?

I know that asserts are not compiled in release mode, but the functions of mathematical classes are time critical and I don't know if using "if" conditions at runtime would be a good solution.

You can build your own asserts to function in release mode, if desired. The performance issue isn't worth worrying about until it shows up in the profiler, so don't stress over a few extra if statements. It is more important that something be correct rather than fast.

As to what you should actually do to cope with the error -- it depends. Probably an exception isn't ever going to be the most ideal solution for something like a math library. How you return a failure indication depends largely on the way the rest of the API is designed (i.e., do you use boost::optional? Do you let the operation continue and produce an undefined result?)

Do you have a concrete example?

Share this post


Link to post
Share on other sites
rip-off    10976
If you are double checking for the programmer, assert is better. Anything that could fail in the wild should be checked with if(). You wouldn't assert() that a file was loaded, because the user could easily delete the file from their hard drive. Be very careful with assert, because any side effects won't be performed during release.

For division by zero, the hardware will issue a fault if this occurs, so I wouldn't bother asserting - although be aware that you or a library can mess with the floating point hardware and disable this check.

Share this post


Link to post
Share on other sites
GregMichael    135
Personally speaking, I'd use an Assert() which can be compiled out for a release build and live with slower speed in the debug build. Get it working first...:)

Regarding exceptions or checking return values, again my opinion - use whatever gives the "end user" the cleanest experience...eg. report errors nicely and with as much information as necessary and do it gracefully.

Just don't crash his PC :)

Share this post


Link to post
Share on other sites
haegarr    7372
IMHO assertions cover another, 3rd kind of failure besides the 2 jpetrie has mentioned in this answer above. Assertions are good for Meyer's Design by Contract, i.e. to check pre-conditions and perhaps post-conditions when a routine is called: "If the calling client guarantees the pre-conditions, then this routine will work fine and guarantees the post-conditions." But you cannot impose each and all runtime conditions to the caller, or perhaps you don't want do so (e.g. due to information hiding).

Share this post


Link to post
Share on other sites
jwein    100
@jpetrie, an example could be the following:

In class Vector3D:

Vector3D& operator/= (float scalar)
{
assert(scalar != 0.0f);

x /= scalar;
y /= scalar;
z /= scalar;
return *this;
}

Or, in class Matrix4D:

void MakeProjectionMatrixFovLH(float fieldOfView, float aspectRatio, float nearPlane, float farPlane)
{
assert(fieldOfView != 0.0f && aspectRatio != 0.0f && (farPlane - nearPlane) != 0.0f);

const float h = 1.0f / tan(fieldOfView / 2.0f);
const float w = (h / aspectRatio);

m[0][0] = w;
m[0][1] = 0.0f;
m[0][2] = 0.0f;
m[0][3] = 0.0f;

m[1][0] = 0.0f;
m[1][1] = h;
m[1][2] = 0.0f;
m[1][3] = 0.0f;

m[2][0] = 0.0f;
m[2][1] = 0.0f;
m[2][2] = farPlane / (farPlane - nearPlane);
m[2][3] = 1.0f;

m[3][0] = 0.0f;
m[3][1] = 0.0f;
m[3][2] = (-nearPlane * farPlane) / (farPlane - nearPlane);
m[3][3] = 0.0f;
}

Here I've used assertions, but I can't be sure that the parameters I used in debug mode will be the same in release mode because they could be loaded by file or in another ways. I don't know if I'm managing well parameter checking in these classes...

Share this post


Link to post
Share on other sites
haegarr    7372
Quote:
Original post by jwein
@jpetrie, an example could be the following:
...
IMHO, those 2 examples are perfect for assertions because they fit fine into Design-by-Contract. The caller has total control because the assertions consider just parameters. Moreover, the caller works on a higher level than the vector/matrix methods, so it has better ways to react on false arguments anyway (and that before (not to say "instead of") calling the vector/matrix method).

Share this post


Link to post
Share on other sites
GregMichael    135
Something else to consider - again just my opinion...

If you're writing a library or code that is to be used by others...then perhaps try and make it bullet proof (idiot proof)...kevlar plate it so that it will handle bad parameters being given to it (by using something at least sensible if applicable or doing something reasonable - if the length of a vector is zero in a Normalise() vector method - return the unchanged vector perhaps ?)

If this is just for use by yourself, then it may be overkill to add anything other than simple checks to prevent errors. In the end the debugger may well step in with a divide-by-zero error and show you where you went wrong for example.

Sometimes a program crash for me...tells me (with the help of the debugger) where I was being a muppet...that's just me though...if I was using a 3rd party library I probably wouldn't be so happy...I'd expect some sort of sensible error handling.

Share this post


Link to post
Share on other sites
the_edd    2109
Quote:
Original post by Patrick Niedzielski
It mostly depends. Either is acceptable, but with exceptions, you have much more flexibility. On the other hand, exceptions are rather slow.


Actually, they can be faster. It depends on the implementation.

In some respects exceptions are actually less flexible; they can be thorn in your side w.r.t to binary compatibility, again because of the different implementations.

</pedantry>

Share this post


Link to post
Share on other sites
Wavarian    850
Because alot of my framework is stored inside its own library file, I choose not to throw exceptions across application boundaries. I think a library should be robust enough to not crap itself when it receives bad input but should also provide feedback for "important" functions (like X.Create(...) etc).

I think within the application itself throwing exceptions is fine, but throwing exceptions because someone tried to enter a negative value into a function that wants a positive value is just silly. I would have the function ignore the input and return - if the programmer wants to know why the function isn't doing its job then they can A) step through it with a debugger, or B) RTFM where it states that negative values aren't supported. Exceptions should be used when an actual unforseeable error occurs that the application can recover from, not to act as intellisense for the programmer.

Just my 2 cents.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this