Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!uunet!aplcen!haven!cs.wvu.wvnet.edu!cerc.wvu.wvnet.edu!babcock.cerc.wvu.wvnet.edu From: vrm@babcock.cerc.wvu.wvnet.edu (Vasile R. Montan) Newsgroups: comp.lang.c Subject: Re: Functions returning Error codes or actual info Message-ID: <780@babcock.cerc.wvu.wvnet.edu> Date: 11 Sep 90 19:41:37 GMT References: <772@babcock.cerc.wvu.wvnet.edu> Sender: news@cerc.wvu.wvnet.edu Lines: 199 As promised, here are the email responses I received on my question on whether functions should return actual data or error codes. It seems the general consensus is to return the error code as the value of the function and return the actual value in an out-mode parameter. Thanks to everyone who responded. -- Vasile ====================================================================== From robert@cs.arizona.edu: Case 1, function value indicates success/failure, usually works well with the enclosing control structure. You won't have to store something into a separate variable and gives a good "functional" approach to your solution: while (get_x(&info)) deal_with(info); or switch (get_x(&info)) { case E1 : case E2 : case E3 : } ====================================================================== From dinl!noren@ncar.ucar.edu: Your raised a real good question, and I'd like to see the responses you get. I waiver all over the place because I don't have a satisfactory solution myself. What I use most often is have the function return the value I want, using a clearly illeagal to flag an error (e.g., return a NULL on a function that returns a string). Prior to the return with the error-value I set the global errno variable to some meaningful value (or have my own extensions to it with a wrapper around perror(3) to display my extension text). In this way I am using (what I interpret, there is such a thing) the UNIX (or is it the C library) philosophy of function error reporting and even have the library functions do much of the work in setting errno for me. For instance, if my socket handler fails, its usually because a library call to the O.S. or C library failed, and that function returns an errno indicating why, which I leave untouched as I return out of my function with an error value. This approach doesn't work all the time, but oh well. Thanks for posing the question. I hope there is a lot of interesting discussion. ====================================================================== From ari@eleazar.dartmouth.edu: In article <772@babcock.cerc.wvu.wvnet.edu> you write: > > I am making a set of functions which return different types of >values: strings, integers, doubles, etc. For example: > char *get_string(); >However, I would also like the function to return an error code if the >function fails. I cannot just return a NULL pointer because I want >the function to be the same as all of the other get_xxx's. I have seen >this done several different ways in C and am wondering if there is an >accepted "proper" way of doing it. I find the easiest to code, most consistent, and most robust approach is to return the error code as the function's result. As in, Error foo(params..., result *output) I then use statements like: err = foo(param, &output); if (err) return(err); /* propagate error until someone can handle it */ For a while, I tried jumping to a label when an error was detected, especially if there was a lot of cleaning up to do. For instance, char *newptr = NULL; char *otherptr = NULL; newptr = (char *) malloc(100); if (! newptr) { err = ERR_MEMORY; goto error; } otherptr = (char *) malloc(200); if (! otherptr) { err = ERR_MEMORY; goto error; } err = do_something(); if (err) goto error; return(0); /* zero is no error */ error: if (newptr) free(newptr); if (otherptr) free(otherptr); return(err); However, the above scheme led to confusing code (of course it did, it has goto's!). I now use a combination of return statements and nested if's. For instance, the above chunk of code would be written: char *newptr, *otherptr; newptr = malloc(100); if (newptr) { otherptr = malloc(200); if (otherptr) { err = do_something(); if (! err) return(0); /* we're now falling through to all of the cleanup code */ free(otherptr); } free(newptr); } if (! err) err = ERR_MEMORY; return(err); Or something like that. Anyway, making everything nest and then falling through to cleanup code seems to be reliable. Of course, this isn't really about error codes, but I thought it would be interesting. -- ====================================================================== From gary@avo.hp.com Tue Sep 11 02:45:35 1990 > Most often in C, I see the error code being returned so it can be used > inside a control statement. This forces the actual information to be > returned in an "out" mode parameter: > CASE 1: if (error_code = get_string(parm, &info)) {...} In the project I've just started, we have agreed on this standard for consistency. There is one exception: if you have a function that would work nicely as a predicate in an "if" statement AND if the error case can be handled reasonably in the return parameter, then you can return the predicate value. An example might help. Let's say you maintain some list of objects. And you want a function IsEmpty(list). Clearly you want to be able to write: if (IsEmpty(List1))... Further, the only likely error case is List1 being a NULL instead of your list-head; and clearly in this case you can return TRUE. Another example: You want NumInList(list). Now, you can use a negative return value to indicate an error. > I have also seen functions which set a global variable to indicate > that an error has occurred: > CASE 2: info = get_string(parm); > if (error_code) {...} I hate this. Using a global like this has lots of problems. If you make another call, you could blot out a useful error code. (This is always a problem with errno in UNIX -- you have to save it before any other I/O clears it.) > CASE 3: info = get_string (parm, &error_code); > if (error_code) {...} I think this is pretty common. You write your functions as if there were no error handling then you append a final parameter for error control. There is nothing wrong with this method. > BTW, I have some other functions which do not return any > information, so they always return an error code. Does this mean I > should use CASE 1 just to keep them consistent? It certainly is one of the advantages that CASE 1 has. ====================================================================== From taumet!taumet!steve@uunet.UU.NET Tue Sep 11 11:51:31 1990 My suggestion is to decide on the abstraction you want for the functions, then write them that way. I would NEVER use a global variable for passing error information. This technique (errno) is used in the standard C library. The ANSI committee recognized this was a horrible idea, but it was existing practice, and could not reasonably be changed. One problem comes when a function which might set the variable calls another which also might set the variable -- the interactions get very hard to manage. Another problem comes in multi-threaded programs or programs with interrupts. You might see the variable set and assume it was your error, when it was an error from some other task. Using an "out" variable for data and returning a status code has some advantages, it seems to me, since you can then write if( get_data(&info) ) { ... ok ... } else { ... fail ... } or switch( get_data(&info) ) { case OK: ... case EOF: ... case ERROR: ... etc ... }