Re: GException notes



Karl Nelson <kenelson@ece.ucdavis.edu> writes:
> There are times like out of memory where a dynamically allocated
> error is kind of a bad idea.  It seems that just having a 
> dtor function which will be set to g_free() for ones generated from
> the standard method and allows for expansion is better.
>

Out of memory is the only one I can think of, and glib doesn't support
out of memory, so no problem. :-)
 
> That is fine, but what about the reverse case.  How do you handle
> if the virtual function in gtk+ returns an exceptioni and has been
> overriden by a C++ function or something similar?  How do
> you reverse the bridge properly?
>

Give me concrete code, I don't understand what you mean. 
 
 
> Actually, that is my key dislike of most of the boxed types.
> Without

I'm not touching this issue, it is a larger problem. :-)

> > If there's no return code then you have to have an indication of fatal
> > error (non)occurrence in the exception itself. How do you intend to
> > indicate this?
> 
> Isn't your exactly the same or am I missing something?
>

No, the point is that in my scheme error != NULL is always a reliable
indicator of whether an operation failed. This is why you can't "pile
up" errors. 

Just write down your proposed API and corresponding conventions, then
see how you meet the three criteria at the top of my original
mail. Then everything will be much clearer.

The error argument looks inconvenient until you see that the global
object requires exactly the same conventions to meet the "must
reliably indicate whether the operation failed" criterion. Namely you
must still clear the error object before entering a function, and you
still can't pile up errors even with a global object.

At that point the advantages of an error argument vs the
strtoul()-style global object are:
 - fewer side effects (because you can create local "error contexts"
   in callbacks and such)
 - cleaner threads situation (you can even pass errors between
   threads)
 - the requirement to explicitly handle or not handle errors so people
   have to think about errors when typing in the function call and
   can't forget to deal with them.

These are not major issues, I agree that the strtoul()-style global
object basically works.
 

> For example should gtk_container_add throw an exception?  There are
> many cases for it to do so, including "we have restricted the data type",
> "it was a bin and you already have one", or "this is a gnome dialog
> and you should use gnome_dialog_add instead." 
>

No, those are all programmer errors. We don't throw exceptions for
that, we g_return_if_fail and spew warnings. This is part of the
premise of GError.

You could change that premise but it would be bad to do so IMO.
  
> Generally, one would think if an exception gets thrown in the middle
> of a signal call the whole signal should be aborted.  So handling
> between signal calls is not really a hard problem there.
>

If the whole signal will be aborted then you don't have an error
pileup situation. 

If you don't have an error pileup situation then my scheme works.
The single global error object also works. You could change the signal
system to be able to handle either of these, though personally I
wouldn't consider it a good change since it would slow down signal
emissions and be more or less useless. Nonetheless it's orthogonal to
the error debate, as long as pileups are not allowed.
 
> Signals are not just for notification in gtk+.  They are a 
> deriviblity, external handling and a notification rolled all
> into one.  I would perfer them to be slightly more separate 
> as I have discussed with Tim, but accept that for simplicity 
> they all share the same framework.  
> 
> Therefore rather than looking at the signal as a notification,
> think about a handler which deals with an incoming data type.  
> There are a lot of things that can go wrong in that handler
> and little way to know which.  Thus exceptions seem likely.  
> And as the handers are mixed with notification this gets ugly.
>

If handlers are allowed to do random things and throw random errors
then you are screwed no matter what system you use.

For example if my gtk_widget_show() signal handler calls
gtk_widget_unrealize() and selects a random value for the
GTK_WIDGET_VISIBLE flag, bad stuff will happen.

If you wanted to have an error-causing signal be extensible, you would
have to adopt conventions that handlers would be required to
follow. One convention might be that the emission stops as soon as any
error occurs, another might be that all handlers have to check that a
previous handler hasn't caused an error, I don't know. In any case,
you can't realistically solve this by creating an error pileup,
because an error pileup breaks an invariant and makes the error
mechanism fail to meet one of the 3 design goals I outlined.
 
> Exception has way too much meaning then.  However the way you
> descript it with functions returning from from the middle and
> checking every action, you are trying to add exceptions by
> convention.
>

Fair description.
 
 
> KISS is an threadsafe int and a array of strings.  man errno :-) 
>

Yes, errno in the strtoul() case is almost identical to my proposal,
they are just two different approaches to basically the same thing.  I
think there are just a few minor arguments in favor of the GError**
arg to functions instead of a global. CORBA and the strtoul() form of
the UNIX API have the same basic system in terms of the conventions
that you have to follow. 

The standard UNIX API is almost the same, except that the "whether the
error occurred" functionality is moved from the state of the error
object to a return code. This is why it can allow error pileups.

Error pileups break CORBA and strtoul()-style UNIX, etc. because you
can't tell if a particular operation failed or not. This is why return
codes are required if you break the no-pileups invariant.
  
> Also since they are sending info back as strings than all you
> can do is spew them.  They are worthless to handler. Should the
> program fallback onto a different file name, should it panic because
> it is about to run out of memory?  Thus a string doesn't seem
> like a really big help.  Especialy and internationalized one
> that you can't even string compare! 
>

You are sending back two things: 
 - a string
 - an error code, which is a module ID plus an error ID
 
I think it's reasonable to add:
 - a "data" pointer that is defined for errors with a 
   particular moduleID/errorID pair and documented to 
   point to a particular type of value.

> That is really bad to do with out seeing what the error is.  
> My connection to X died, isn't going to be easy to read from
> the non-existant popup.  :-)
>

You know from the documentation/specification of the function whether
"connection to X died" is a possible error as long as errors are only
passed up from the function being invoked. See any UNIX API man page.

This is indeed exactly the problem if you have a global error stack,
and some random callback could have inserted errors on it. You would
have to handle all possible errors in all error handlers. To find out
if your operation failed you would need to scan the stack and decide
which errors might have come from your operation. Basically a global
error stack is just syslog(), it is not an error handling scheme.
Yes you can find particular errors in a syslog, but it's no fun.

If you and Tim go back to my original mail and look at the three
design goals at the top, things will be much clearer.

 - do you agree with the design goals?
 - if no, what should the goals be?
 - if yes, then how does your proposed system meet them?

I contend that the breaking the no-error-pileup invariant hopelessly
destroys one of the design goals. That is why I keep emphasizing it.

Again, the three goals are:
 - being able to programmatically identify a particular error, 
   by moduleID/errorID pair, and special-case that error
 - being able to know if a particular operation experienced a fatal 
   failure (would it have returned -1 in the UNIX API?)
 - having a nice human-readable error message, potentially containing 
   details such as the name of the file which wasn't found, or the 
   surrounding context of a parse error

If you want to add complexity beyond what's required for those goals,
I think you need to first add some additional goals that you are
trying to achieve.

Havoc



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