Sign in to follow this  
HexDump

Exceptions and Return codes (yes sorry, agian).

Recommended Posts

Hi all, I have read lota of information about exceptions, lota of threads here about them and return codes, but I can´t decide what to do. It seems the best thing is to mix Exceptions and return codes, and user Exceptions only for really important errors (pretty hard to decide what are they). So, Could anyone expose its point of view, and give any example about how to use all this in correct way?. Best practices, etc... Thanks in advance, HexDump.

Share this post


Link to post
Share on other sites
That link is a good one, and Fruny's link within that link is also good.

I agree with Sit in the link that the most important thing is to be consistent. I throw a lot more in Java than in C++ for example. Personally, I don't buy into the 'Exceptions only for exceptional circumstances'. If you have an error in a function that the caller needs to know about, that's exceptional enough for me. Though that doesn't always call for an exception.

The other options aren't really great to me either. A bool usually isn't too useful and will often get ignored leading to bugs. I'll use these for something like Container.Remove(Item) where the success might be useful to know, but ignoring it won't be the end of the world.

Returning null/error-value on failure is similar, but (imo) better since the caller can't really ignore the return value since that's often what they want in the first place.

A static errno style error code sucks, and should be avoided.

A returned error code is (imo) not so good either. It leads to a lot of case statements where-ever the method is used, or a catchall "handle this error code" method which makes stuff unclear and icky. Plus it somewhat defeats the purpose of a function (take input, produce output).


But that assumes the caller needs to know about the method's failure. That isn't always the case, and (imo) should be the preferred design. If a player doesn't have permission to do something in-game for example, you can handle that within the method (perhaps with a configurable event) or just no-op and log it.

But yeah, check out those links. People far more seasoned and intelligent than I might think otherwise (and have better reasons why).

Share this post


Link to post
Share on other sites
Hi again,


ReadTFM, mate, don´t understand what you want to mean :). Why would you do stack unwinding by yourself?

Another question that goes paralell to this question: Where should a catch go, I mean, I don´t feel like writing try/catch for every everror I want to check, do anybody know (I don´t know if this a stupid question) where catches should go? (I have read they execptions should be using for centralized processing of errors, so, there should be some key places where put them).


Thanks in advance,
HexDump.

Share this post


Link to post
Share on other sites
Quote:
Original post by HexDump
Another question that goes paralell to this question: Where should a catch go, I mean, I don´t feel like writing try/catch for every everror I want to check, do anybody know (I don´t know if this a stupid question) where catches should go?


Always place a catch statement in a place where you can handle the exception that you catch.

Share this post


Link to post
Share on other sites
Quote:
ReadTFM, mate, don´t understand what you want to mean :). Why would you do stack unwinding by yourself?


Sometimes a failure needs to be propagated up several levels. Instead of having the three or four or whatever functions test for return codes and then return an error code themselves, it's easier to throw an exception that will jump through the stack all the way up to the top level caller.

Share this post


Link to post
Share on other sites
ToohrVyk, sure you said something pretty logical for all of you, but could you put any example? it doesn´t need to be code, just a practical example, because I think put a catch in every try won´t be the right way to go.

ReadTFM, ok, undestood :).

Thanks in advance,
HexDUmp.

Share this post


Link to post
Share on other sites
Consider a function which reads an object from a network connection, and returns that object. If the network connection dies (throwing the relevant kind of timeout or EOT exception), there's nothing the function could do to return a valid object. Thus, the function must not catch the exception, and simply let it move up the call stack instead.

On the other hand, if the function is about a server connecting players to the game, then there's a pretty simple thing to be done when the connection breaks down: remove the player from the game, and possibly log the issue or broadcast a relevant message. Thus, the function should catch the exception, because it can solve the issue.

Share this post


Link to post
Share on other sites
RAII is another approach that mandates exceptions.
NonNegativeNumber p(user_input);


If user enters -18, p cannot be created, exception is thrown, and execution from then on is impossible.

While there are alternatives, the error handling here is robust and transparent.

One might however opt for allowing invalid input, but the question remains - what is a good replacement? We cannot accept negative number, but we need to replace it with a default value.

But in this case, we have removed the exceptional condition and changed the flow. Exceptions are one tool that is used to enforce logical correctness of application.

Share this post


Link to post
Share on other sites
Quote:
Personally, I would look into replacing that with a value that clamps the range to min/max (thus values below/above become min/max) and a method to accept user input (doing logic if invalid, or just letting the clamped value do its thing). That sort of separation of responsibility is often available in situations that arise as possible exception/error cases.


As I said, you can change the original problem.

My example was more focused on code-path if you use invariants.

NonNegativeNumber p(user_input);
...
// p is valid, non-negative


Sometimes, changing the problem is viable, sometimes it's not.

A more realistic example:
void send(SomeMessage &msg)
{
Buffer b(500); <- allocation succeeded
Serializer s(msg, b); <- serialization of msg into b succeeded
socket->sendto(s->contents, s->size); <- s is valid
}

void notifyClients()
{
StateChangeMessage msg( who, 17, 0, "new_state", 0); <- parameters are correct
send( msg ); <- msg is valid
}

...
try
{
...
notifyClients();
...
} catch ( ??? ) {
// A *lot* of things can go wrong
// but if we fail, we can't do much
}



Many things can go wrong - but code line by line establishes invariants, allowing you to bypass many/most/all user-level checks and assertions.

With error codes, this would result in a very large number of tests, error reports and conditions, which in case of RAII are conveniently abstracted and encapsulated, allowing for a much more straight-forward code.

But whichever approach you choose, you impose one coding style, hopefully a consistent one. In case of RAII and proper encapsulation, you can perform almost all error checking within implementation of classes themselves, thereby staying very OO oriented.

Share this post


Link to post
Share on other sites
Heh, sorry. Antheus quoted my post which I deleted before he could reply since it was nitpicky and focusing on the example rather than the assertion regarding exceptions + RAII (which is correct)

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