Re: GException notes



Tim Janik <timj@gtk.org> writes: 
> havoc, you should make your mind up on whether you want some genuine errno
> improvement for *one* level function calls or a flow-control affecting
> error handling mechanism.
> 

There is no way to affect flow control without longjmp().

When I say that after an error you must handle it or return
immediately, this is a convention for sane use of the error system,
but it is not enforced by the API. If you use these conventions you
can effectively pass up errors, like this:

void foo(GError** err);
void bar(GError** err);
void frobate(GError** err);

void frobate(GError** err)
{
  GError* myerr = NULL;
  foo(&myerr);
  if (myerr != NULL)
   {
    /* can write a convenience function or macro for this */
    if (err)
      *err = myerr;
    else 
      g_error_destroy(myerr);
    return;
   }

  bar(&myerr)
  if (myerr != NULL)
   {
     g_error_set_and_destroy(err, myerr); /* with convenience function */
     return;
   }

  return;
}


> 2) if you don't go with setjmp/longjmp code (which we definitely won't do
>    in glib/gtk+ [wink to owen]) but still want to come close to an
>    exception handling like scheme, you'll have your callers normally execute
>    untill they terminate (we need to, because there's no such thing as a
>    closure dtor in C) and thus you have to care about *multiple* errors to
>    occour in an callback driven system before you actually get around to
>    handle any of them


I still see no concrete example of allowing multiple errors to return
from a single function to a single caller. Why do you want that? It
makes no sense. This is my point. Now, you can do this:

void frobate(GError *err)
{
 /* This code could happen to be inside a callback also */
  GError *my_err = NULL;
  do_stuff(&my_err);
  if (my_err != NULL)
   {
     handle without affecting "err"
   }

  /* potentially set err down here for other fatal reasons */
}

here you have no error pileup, though you do have multiple errors.
This is one advantage of not using a global variable.

The stack does not solve the pileup problem, because the pileup
problem is that a pileup prevents you from knowing if a particular
operation failed. The stack prevents that, as do random settings of
the global errno in callbacks. A return code can provide this
information if the global error object is no longer a reliable
indicator. In UNIX the global error object is not a reliable indicator 
so you have to have a return code.

i.e. my point is that in my scheme, error != NULL is always a reliable
indicator of whether the operation failed. If you break this by using
a global object and calling unrelated operations during the frobate(), 
then you must provide another indicator of failure.

GError* error = NULL;
frobate(&error);
if (error != NULL)
   /* frobate() definitely failed, error != NULL is not a side effect
      of some callback */;

> the point that you don't seem to have gotten is: for an *exception-like*
> scheme, you *must* deal with cases where multiple errors occour before
> you can handle them.
>

But you should not deal with that by passing the errors to unrelated
code sections via a global object.
 
> but as said already, you probably didn't have an exception like thing
> in mind, though on the other hand you added things like a module key to
> your struct _GException (which isn't needed for *one-level* calls with
> error handling), and were talking on "What information an exception
> contains" with regards to "the Unix API".
> 

The module key is potentially useful if a given call to frobate()
invokes subroutines from another module and passes them back up to
you. Say foo() in the above example is in a different library from
frobate(). Or say a gnome-libs function calls both GConf and
gdk-pixbuf functions and can fail for either reason.

> now step back and rethink the issue, do you want to improve error reports
> from immediate callees or do you want to alter normal flow-control of
> a program (i.e. exception handling), which are two distinct and *very*
> different things.

I want to have error reporting for immediate callees, but you need a
way for immediate callees to easily pass up errors caused by
subroutines that those immediate callees invoke. i.e. you want
frobate() to easily be able to "trickle up" errors caused by foo().

This is very analagous to exception handling in conjunction with the
must-return-immediately-or-handle convention. i.e. errors are either
passed up the stack or handled in the current stack frame, and there
can only be one error at a time.

The key attributes of exceptions in real languages, and also this
API/convention set is that the three criteria I outlined in my
original mail are met. A global error object with strtoul()-style
conventions also meets the criteria.

Havoc



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