Skip to main content
gamedev.net
🔒 Locked

Dealing with multiple cases returned by a function.

Started by d1sc0rd Jan 10, 2005 at 12:04 PM 21 replies 3.3k views
Original Post
d1sc0rd
d1sc0rd
I had a few simple questions. -In c/c++, do you normally have a function return 0 for success or failure? -How does a function return an integer value based on a calculation, and still return multiple failure states? (Edit: Summary of responses in my last post.) [Edited by - d1sc0rd on January 11, 2005 4:21:06 PM]
Daaark
Daaark
Quote:
Original post by d1sc0rd
I had a few simple questions.
-In c/c++, do you normally have a function return 0 for success or failure?

-How does a function return an integer value based on a calculation, and still return multiple failure states?


Some return negative values for errors.

Some, return only true or false, and you give the function a variable to store the answer.
ToohrVyk
ToohrVyk
Or better yet, if you are using C++, use exceptions.
3DNeophyte
3DNeophyte
For a function returning 0 for success or failure depends on the function. Functions such as strcmp() will return 0 if the strings match, but some will return 0 to mean "false", or error.

As for your second question, functions can return multiple states using the return value and pointer arguments. fread(), for example, will return the number of bytes read and will also "return" the filled buffer through the pointer passed in: int bytes = fread((int*)&my_int, sizeof(int), 1, fp);
Which in that case, bytes should be 4 and my_int gets filled with an appropriate integer value. Also, a lot of system calls will have an errno argument (a pointer) that you can evaluate for failure conditions as well as return an integer directly from the function.

Hope that helps.
d1sc0rd
d1sc0rd
Thanks guys. That helps a lot.
Martin Foerster
Martin Foerster
Quote:
Original post by ToohrVyk
Or better yet, if you are using C++, use exceptions.


I discourage using exceptions for controling program flow.
Like the name implies exception handling is used for exceptions, nothing less!

If you test your code and know what you are doing you are better off without them.

The only area where I would use exceptions are code paths which are suspect to internal errors that may not be recoverable by the code using them, for example accessing devices in a low-level-fashion.

In every case, you have to consider the cost involving exceptions, and the advantages they offer.
(In most cases performance will win)
There are 10 kinds of people,those who understand binaryand those who not.
Emmanuel Deloget
Emmanuel Deloget
Quote:
Original post by Martin Foerster
Quote:
Original post by ToohrVyk
Or better yet, if you are using C++, use exceptions.


I discourage using exceptions for controling program flow.
Like the name implies exception handling is used for exceptions, nothing less!


Well, in most cases, errors should be exceptions :) I mean, I don't want my program to fail everytime I launch it and exceptionnaly do the right thing [grin].

If you code using C++, then you may either use exception or return an error code - but if you use exceptions, please read Herb Sutter advices on GotW (#8, #21, #47, #56, #59-#60, #65, #66, #82 - as you can see, Sutter have a lot to say about exception. It is clearly an indication of the complexity of safe exception using), before implementing them.

If you want to return an error code, then you can do it in two ways:

1) return a integer which can represent either an error or your result. Remember we are speaking of C++ and type safety. How can you safely consider that an error code and a integer value are equivalent and can share the same variable? It is far too dangerous.

2) return the error code (some enum, for example - I strongly encourage you to use a type-safe enum-like class, this will allow you to do some extra work with the error data) and use a output parameter as the output of the function, like this:
const ECode& doSomething(int& retvalue);

This syntax enables you to write
if (doSomething(myvalue) == ECode::thisIsSoBad) {  // error management code} else {  // normal code, which uses retvalue.}

the bzip2 library uses a similar mecanism - except that the error code is the output param, which seems rather strange to me.

I don't know any reference that will tell you what is the right C++ way to do it. If you don't want to use exception, I suggest you to prefer (2) instead of (1).

HTH,
d1sc0rd
d1sc0rd
Emmanuel Deloget-
I just want to make sure I am getting this conecpt right. You are suggesting that, as an alternative to using the buly built in exception handling, I use my return values to report enum type error messages when the function failes, and use pointers to input variables in order to change values in situations when the function succeeds?
Emmanuel Deloget
Emmanuel Deloget
Quote:
Original post by d1sc0rd
Emmanuel Deloget-
I just want to make sure I am getting this conecpt right. You are suggesting that, as an alternative to using the buly built in exception handling, I use my return values to report enum type error messages when the function failes, and use pointers to input variables in order to change values in situations when the function succeeds?


Yes, this is what I said. It replace the somewhat horrible "in case of error, return -x, else return the correct value".

ECode doSomething(int& retvalue){  // blah blah  if (everythingOk) {    retvalue = someValue;    return ECode::ok;  } else {    return ECode::somethingBadhappened;  }}

It is simple enough to write and is clear enough to be understandable in any situation.

But if you believe exception is the right thing to do, then don't bother what others said. It is possible to write good exception handling code. I believe they are not so bad - they just have to be correctly implemented, and that's the hard part.

Regards,
blizzard999
blizzard999
In my opinion a function should never fail so you should test if it is safe to call that function.
The case in which the function fails is an 'exception' and it should resolved with exceptions.
C++ exceptions allow good style code because if you call several nested functions and you throw an exception in the last the stack is unwinded 'automagically'.
You can handle the exception only at the higher level without read and repeat several returning error codes.

Another case (bad because one should use std::)

Pseudo-code
...FILE* f1 = NULL;FILE* f1 = NULL;FILE* f3 = NULL;if(!OpenFile(f1)){  return(err);}if(!OpenFile(f2)){  CloseFile(f1);  return(err);}if(!OpenFile(f3)){  CloseFile(f1);  CloseFile(f2);  return(err);}// continue


with exception

// I suppose that OpenFile() throw an exception if it fails!FILE* f1 = NULL;FILE* f2 = NULL;FILE* f3 = NULL;try{  OpenFile(f1);  OpenFile(f2);  OpenFile(f3);}catch(...){  CloseFile(f1);  CloseFile(f2);  CloseFile(f2);  // you can throw another exception or return an error code}// continue

blizzard999
blizzard999
Quote:
Original post by d1sc0rd
I had a few simple questions.
-In c/c++, do you normally have a function return 0 for success or failure?


You can do what you prefer. However this is common.

Quote:

-How does a function return an integer value based on a calculation, and still return multiple failure states?


You cannot do this! The only solution is to store the error code in the low/high part of the integer you dont use.
You must pass a ptr to an error_handler and use it to store extra info...

d1sc0rd
d1sc0rd
Is there any disgnifcant difference between these two concepts? Any reason to choose one over the other?

enum ErrorCode  ErrorCode ECode doSomething(int& retValue)  int retValue doSomething(ErrorCode& ECode)
Endurion
Endurion
Quote:
Original post by blizzard999
In my opinion a function should never fail so you should test if it is safe to call that function.
The case in which the function fails is an 'exception' and it should resolved with exceptions.
C++ exceptions allow good style code because if you call several nested functions and you throw an exception in the last the stack is unwinded 'automagically'.
You can handle the exception only at the higher level without read and repeat several returning error codes.

Another case (bad because one should use std::)

Pseudo-code
*** Source Snippet Removed ***

with exception

*** Source Snippet Removed ***


You're aware that your exception code is different from the code before?
Your code behaves like this:
FILE* f1 = NULL;FILE* f1 = NULL;FILE* f3 = NULL;if ( ( !OpenFile( f1 ) )||   ( !OpenFile( f2 ) )||   ( !OpenFile( f3 ) ) ){  CloseFile( f1 );  CloseFile( f2 );  CloseFile( f3 );  return( err );}


To have the exact same behavior you'd need to have three try/catch-blocks.
Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>
Zipster
Zipster
Quote:
Original post by d1sc0rd
Is there any disgnifcant difference between these two concepts? Any reason to choose one over the other?

*** Source Snippet Removed ***

Usually it's better to have the error code be returned, and any other outputs be passed as reference/pointer parameters. The logic is that you should really only need a single value to indicate failure or success (including different types of failures and successes), so it fits naturally as a return value, while any other outputs become parameters.
blizzard999
blizzard999
Quote:
Original post by Endurion
You're aware that your exception code is different from the code before?
Your code behaves like this:
FILE* f1 = NULL;FILE* f1 = NULL;FILE* f3 = NULL;if ( ( !OpenFile( f1 ) )||   ( !OpenFile( f2 ) )||   ( !OpenFile( f3 ) ) ){  CloseFile( f1 );  CloseFile( f2 );  CloseFile( f3 );  return( err );}



They are equivalent but this is only a very simple example.
My intent was only to show how exception can resolve "goto error handler" problems...

Quote:

To have the exact same behavior you'd need to have three try/catch-blocks.


This is not true because the first throw causes a jump to the first catch-all statement and I'm sure that subsequent OpenFile() are not executed.
But I'm not sure that EVERY compiler does not execute subsequent || if the first is true.
Enigma
Enigma
Quote:
Original post by blizzard999
Quote:
Original post by Endurion
To have the exact same behavior you'd need to have three try/catch-blocks.

This is not true because the first throw causes a jump to the first catch-all statement and I'm sure that subsequent OpenFile() are not executed.
But I'm not sure that EVERY compiler does not execute subsequent || if the first is true.

In the non-exception example in the event of an error you close only files that have been opened. In the exception example you always close all files even if the latter ones were never opened. BTW, operator|| is guaranteed by the standard to short circuit:
Quote:
C++ Final Draft Standard
5.15.1 The || operator groups left-to-right. The operands are both implicitly converted to bool (clause conv). It returns true if either of its operands is true, and false otherwise. Unlike |, || guarantees left-to-right evaluation; moreover, the second operand is not evaluated if the first operand evaluates to true.

(emphasis mine)

Enigma
_the_phantom_
_the_phantom_
tbh, the whole example was pretty poor, a mixture of C-style and C++ is asking for a world of hurt in this case [smile]

My current project uses exceptions to signal that events have happened which, normally, shouldnt happen such as a failure to intialise a window, a failure to setup an OpenGL mode when we know it to be workable, a failure to swap buffers and even a few std::logic_error cases when the programmer tries to use the system in an illogical manner.

The rational behind it being that if any of those events fail things have gone very wrong somewhere and aborting is the best way to go and in some cases you dont want to write code to check things.
For example, each frame I could check that SwapBuffers() worked or I could assume it has and if it fails THEN deal with the problem (which is probably a show stopper anyways).

As for the OP's question;
If a function can only have 'success' or 'failure' options then I'd be more likely to return a bool, unless an error code system was needed, at which point its Enum time, with -1 being 'success'.

The 2nd comes down to design, if the failure of that function is critical and shouldnt happen then exceptions, if it can happen and is non-critical (say the loop can keep on running fine) then I'd be inclined to go with the solutions presented here, as to which one depends on the situation at hand.
blizzard999
blizzard999
Quote:
Original post by Enigma
In the non-exception example in the event of an error you close only files that have been opened. In the exception example you always close all files even if the latter ones were never opened.


CloseFile(NULL) should be a NOP...

void CloseFile(FILE* f){
if(!f){return;}
...
}

Quote:

BTW, operator|| is guaranteed by the standard to short circuit:
Enigma


OK...now I'm sure! :)

----------------
See also
http://www.parashift.com/c++-faq-lite/exceptions.html
Fruny
Fruny
Without getting involved in the return code vs. exception debate (both have their place, it is your duty to know them), it is idiomatic for a function to return 0 on success and non-zero on failure.

Why? Because a function can have multiple failure modes and thus return different error codes for each, while knowing that the function succeeded is generally sufficent, and does not warrant multiple codes for succes.

There is only one zero, and many non-zero values.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.