Path: utzoo!attcan!uunet!aplcen!uakari.primate.wisc.edu!zaphod.mps.ohio-state.edu!rpi!bu.edu!husc6!redsox!campbell From: campbell@redsox.bsw.com (Larry Campbell) Newsgroups: comp.lang.c Subject: Re: Functions returning Error codes or actual info Message-ID: <1591@redsox.bsw.com> Date: 13 Sep 90 02:44:26 GMT References: <772@babcock.cerc.wvu.wvnet.edu> <1990Sep11.121531.23065@jarvis.csri.toronto.edu> <285@grapevine.EBay.Sun.COM> <1990Sep12.105622.847@jarvis.csri.toronto.edu> <291@grapevine.EBay.Sun.COM> Reply-To: campbell@redsox.bsw.com (Larry Campbell) Organization: The Boston Software Works, Inc. Lines: 60 Magic Values are a Bad Thing. You have to remember to check the returned value to see if it's a Magic Value, and if you forget to check (a very common mistake), your code can break in mysterious ways. For example, who *really* bothers to check *every* call to malloc to see if the returned value is NULL? Magic Values also mean you often have to pervert the type system -- like making fgetc() return an int instead of a char (what beginning C programmer has not made the mistake of declaring its result as a char?) A much better approach is to use exceptions, a la Modula-3. Functions are assumed to *always* return the asked-for value. If an error occurs, an exception is raised; *if* you're prepared to check for the error, you declare an exception handler which gets control; otherwise, the default exception handler takes over, which usually just tells you the name of the exception, and where it occurred, and dumps core. I have implemented a portable exception handling facility for C that we are using in all our projects here and have found to be very useful. Using its syntax, here is an example: 1 TRY 2 FOO x; 3 x = get_next_foo(); 4 fondle_foo(x); 5 EXCEPT 6 CASE(Exc_No_More_Foos) 7 printf("all foos fondled!\n"); 8 return; 9 CASE(Exc_Foo_Quota_Exceeded) 10 printf("buy more foos!\n"); 11 exit(1); 12 ENDTRY get_next_foo() is defined *always* to return a FOO, or to raise an exception. If get_next_foo() runs out of FOOs, for example, it just says: RAISE(Exc_No_More_Foos) This will cause control to pass immediately to line 7 above. This RAISE statement could occur inside get_next_foo(), or inside any routine called by get_next_foo(). If an exception is raised that is not listed in the EXCEPT clause (in the present example, any exception other than Exc_No_More_Foos or Exc_Foo_Quota_Exceeded), the stack is unwound until a TRY-EXCEPT clause is found that wants to catch the exception. If no willing handler is found, the default exception handler is invoked (which in our implementation prints the name of the exception and the source file name and line number where it was raised, and then dumps core.) Using this facility, it is "safe" not to bother to check for the success or failure of a function call; by "safe" I mean that the behavior of the program is well-defined -- it crashes immediately with a message that tells you exactly what happened. If your functions return Magic Values and you forget, or just don't bother, to check for them, then when they fail, your program's behavior becomes undefined. -- Larry Campbell The Boston Software Works, Inc. campbell@redsox.bsw.com 120 Fulton Street wjh12!redsox!campbell Boston, MA 02109