lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


It was thus said that the Great Andrew Starks once stated:
> 
> AND, I think i'm mostly talking about internals, anyway. The motivation of
> my question stemmed from having trouble dealing with errors in a sane way.
> How I deliver them to customers is somewhat (but I guess not really) of a
> different question, which should be more heavily influenced by conventions.

  I know my way of dealing with errors is somewhat controverial [1] and all
error handling schemes come down to three cases:

	1) return nil,error
	2) throw an exception
	3) ignore the error and proceed as if nothing happened (which on
	   most systems nowadays will probably lead to #2 anyway)

  But I haven't noticed anyone approaching this from the other
angle---writing code as to avoid having to handle errors.  

  When I write a library (for instance), I assume I will get valid data, and
I will try my darndest to return valid data to the caller.  So, for
instance, my DNS library [2], I have:

	dns_rcode_t dns_encode(
	        dns_packet_t      *const restrict dest,
	        size_t            *const restrict plen,
	        const dns_query_t *const restrict query
	)
	{
	  struct idns_header *header;
	  uint8_t            *buffer;
	  block_t             data;
	  dns_rcode_t         rc;
	 
	  assert(dest  != NULL);
	  assert(plen  != NULL);
	  assert(*plen >= sizeof(struct idns_header));
	  assert(query_okay(query));
	

  I assert() the parameters.  I am assuming you are giving me what I ask. 
Yes, I could have done:

	if (dest == NULL)
	  return RCODE_FORMAT_ERROR; /* or something more appropriate */

	if (plen == NULL)
	  return RCODE_something;

	if (*plen < sizeof(struct idns_header))
	  return RCODE_somethingelse;

	if (!query_okay(query))
	  return RCODE_blahblahb;

but in my opinion, doing such "defensive programming" [3] hides bugs (why
would you be calling my routine with NULL pointers anyway?).  The calls to
assert() catch improper calls to the routine (and in at least two cases I
know of, actually caught bugs in other people's code using the library). 
That's not to say errors still can't happen (you can still have bad domain
names, or not a big enough output buffer) but I feel like I've reduced the
list of potential errors.

  I do not use exceptions much, having come from Assembly and C which
doesn't really have exceptions [4], and because of that, I tend to do option
1 (and for throw-away stuff like simple scripts, option 3).  But in a
language that supports both (and you can do exceptions in Lua), I would
stick with the "return nil,error" method unless something comes up I didn't
expect and for which I have no idea how to handle, and thus, an exception
might be called for.  Maybe.  

  Personally, I view "exceptions" like CPUs view "exceptions" (division by
0, trying to execute a priviledged instruction, or an unimplemented
instruction)---really bad programming errors that should cause the running
program to stop because there is no meaningful way for the program to
continue.  But that's me.

  -spc (Who doesn't like that signals have evolved past CPU exceptions)

[1]	Like from this exchange on the mailing list last year:

	http://lua-users.org/lists/lua-l/2013-12/msg00022.html

[2]	https://github.com/spc476/SPCDNS/blob/master/src/codec.c

[3]	I do have one story about being too defensive in coding.  At work,
	the senior developer, in certain critical portions of code, blocked
	all signals from being delivered (POSIX system and all). 
	Occasionally, the code would a SIGSEGV (hitting bad memory) but it
	was hard to track down the exact cause, *because he blocked all
	signals in certain portions of the code!* The actual cause was in a
	critical section, but it was hard to catch because it was never
	generated where it actually happened.  He grumbled, but eventually
	fixed the code to be "less defensive."

[4]	For assembly, it really depends upon both the architecture and
	operating system; for C, you can kind of fake exceptions with
	setjmp()/longjmp() but it's hard to get right.