Re: GException notes



On Tue, 1 Feb 2000, Karl Nelson wrote:

> void g_exception_catch ();  /* sets global handling on */
> void g_exception_set (key, const gchar*,...);  
>         /* set exception, destroys current if set */
> void g_exception_set_static (key, const gchar*);
> void g_exception_set_full (key, const gchar*, GExecptionDtor);
> gint g_exception_try(key, Handler);  /* clears exception */
> gint g_exception_is_set();           /* indicates an exception */  
> 
> #define g_exception_pass() if (g_exception_is_set()) return
> #define g_exception_pass_val(X) if (g_exception_is_set()) return (X)

> int 
> frobate(gint blah, gint blah2, gint blah3)
> {
>    if (!do_sys_stuff())
>      {
>        /* key is frobate itself for this exception */ 
>        g_exception_set(&frobate, _("Error do_sys_stuff fail %s"), errno);
>        return FALSE;
>      }
>    /* ... */
>    ferby();
>    g_exception_pass_val(FALSE);  /* throw up to next layer */
>    
>    /* ... */
>    return FALSE; 
> }
> 
> void 
> use_frobate(gint blah, gint blah2)
> {
> 
>    g_exception_catch();
>    frobate(blah,blah2,NULL);
>    g_exception_try(&ferby, FerbyHandler);
>    g_exception_try(&frobate, NULL);
>   
>    /* other wise the exception will be destroyed on the next catch */
> }

hm, except for you reversing the meaning of _try() and _catch(), this
approach does look far more appealing to me than havoc's.
however, i don't think using the function pointers as a key is a very
good idea, this will require incident knowledge about the callees
implementation on the caller side, which is breaking the abstration
level to say the least.

first, i don't think we should be talking about exceptions here at all
if we don't consider some setjmp()/longjmp() hackery, because intermediate
functions aren't affected (properly handled) at all here for calling
sequences like:

A() {
  TRY {
    B ();
  }
  CATCH...
}

B () {
  C ();
  stuff1 ();
  D ();
  stuff2 ();
}

C() { THROW (foo); }
D() { THROW (bar); }

without setjmp/longjmp constructs, stuff1() and stuf2() will always
be executed, no matter how hard C() and D() try to THROW things
around.
while havoc is about to jump up at this point to tell that B() is
incredibly wrong coded because it doesn't CATCH after calling C(),
this is exactly the situation you'll find most often in gtk+ code.
consider A() some user function that causes a signal emission in
its TRY block, B() being signal_emit() and C() and D() being user
defined handlers.

using setjmp/longjmp, we could add enough hackery to the signalling
core to prematurely abort emissions and return after calling C(),
so A() could try do some recovery tricks and retry or simply puke
on the user. but while that would plug the obvious Dtor lack of C closures
for the signal core, it is not really a viable approach for a generic
library like GLib which comes with a huge amount of callback facilities,
each of which would require the same amount of hackery, as well as third
party code that makes use of glib and calls user code.

so since we're not going to implement a mechanism to hard-break the
usuall flow control of a program, i think the name "exceptions" is
majorly misleading here.

rather, something like a global error entity that could be set would
be more appropriate, but still doesn't satisfyingly solve the above
case where C() and D() have error conditions that we might want to
see handled by A().

so to top that, the basic idea to initially start out on should be
soemthing like an error stack that can be accumulated in callbacks
and be (selectively) handled/discarded at higher function levels that
care.

so suppose we are actully talking about some kind of error stack here,
there's another interesting thing that would be nice to have, but has
only been slightly touched by havoc's initial proposal:
type variant errors

the most obvious error types are definitely nice descriptive strings or
raw integer error codes, but something to learn from the higher level
languages is to have user-defined error objects if desired.
even if we don't have some GObject base class at this point (that
user defined error classes could be derived from) it is definitely something
to keep in mind when coming up with a generic improved error handling
mechanism at this point.

so for instance, the mechism i'm brainstorming about here (didn't actually
sit down and really design this yet) could look like so:

void     g_error_stack_push_int    (gint         error_code);
void     g_error_stack_push_printf (const gchar *fmt,
                                    ...);
gboolean g_error_stack_is_empty (void); /* merely an alias for
                                         * g_error_stack_type() == G_TYPE_NONE
                                         */
void     g_error_stack_clear      (void);
GType    g_error_stack_type       (void);
GType    g_error_stack_ftype      (void); /* fundamental type */
GType    g_error_stack_type_is_a  (GType is_a_type);
gint     g_error_stack_pop_int    (void);
gchar*   g_error_stack_pop_string (void); /* return value may be used untill
                                           * the next g_error_stack_pop()
                                           */

which would allow for functionality like:

A() {
 g_error_stack_clear ();
 B ();
 while (!g_error_stack_is_empty ()) {
   switch (g_error_stack_ftype ()) {
   case G_TYPE_INT: g_print ("puke %d\n", g_error_stack_pop_int ()); break;
   case G_TYPE_STRING: g_print ("puke %s\n", g_error_stack_pop_string (); break;
   case G_TYPE_OBJECT: do_something_with_objects (); break;
   default: /* can't handle, pass on */ return;
   }
 }
}

B () {
  C ();
  D ();
}

C() { g_error_stack_push_int (EPERM); }
D() { g_error_stack_push_printf ("i fell sick today"); }


while this may not exactly be what havoc and karl had in mind originally
(and doesn't come with as much syntactic sugar), it actually comes far
closer to our needs in a strongly callback-influenced environment.
and noticably isn't by any means more complex, though still allowes for
a far more flexible handling mechanism.

interesting aspects that should be furtherly investigated are whether
we really want the error list to be stack with FIFO behaviour (LIFO
could be interesting here as well), and whether it'd make sense to be
able to provide priorities or fatality flags for distinct errors.

> 
> It seems to me that adding lots of exception pointers to the end
> of functions will just make life very hard, especially for connections
> and backward compatibility.

not to mention varargs functions which want to report errors, but
had to break with that convention.

> 
> --Karl  
> 

---
ciaoTJ




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