Re: GException notes
- From: Karl Nelson <kenelson ece ucdavis edu>
- To: gtk-devel-list redhat com
- cc: kenelson sequoia ece ucdavis edu, kenelson sequoia ece ucdavis edu
- Subject: Re: GException notes
- Date: Tue, 01 Feb 2000 23:31:43 -0800
>
> Karl Nelson <kenelson@ece.ucdavis.edu> writes:
> > Then I don't understand how you intend to handle the memory at all.
> > You are going to take a const format string and a set of arguments
> > and print into some sort of char array. Now unless that is a
> > static array for all exceptions, this means that that for some it
> > must be duped.
> >
>
> It's never a static array, it is always on the heap. See
> gconf/gconf/gconf-error.c.
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.
> > What about languages wrappers which are going to return these
> > exceptions potentially? They will likely allocate resources
> > differently and thus need a dtor to clean up their memory.
> >
>
> class G_Exception {
> G_Exception(GException* e) : exception_(e) {}
> ~G_Exception() { g_exception_destroy(exception_); }
> G_Exception(const G_Exception& src) {
> if (exception_ == src.exception_)
> return;
>
> g_exception_destroy(exception_);
> exception_ = g_exception_copy(src.exception_);
> }
>
> string str() const { return string(exception_->str); }
> gint num() const { return exception_->num; }
> }
>
> // this following is trivial to machine-generate
> void cpp_wrapper_for_func(gint blah, gint blah2)
> {
> GException** exc = NULL;
> func(blah, blah2, &exc);
>
> if (exc != NULL)
> throw G_Exception(exc);
> }
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?
> > The other key advantage to having a dtor is that the exception
> > can be derived if need be. (Thus allowing bindings freedom to
> > pass additional info through if they need to.)
> >
>
> I do see the need for a destroy notify on user data for the exception,
> if we add a user data field.
>
> None of the other boxed types have this generic destructor so I am
> reluctant to say GException is different. Should all boxed types have
> a gpointer extra_binding_data with a destroy notify function?
Actually, that is my key dislike of most of the boxed types. Without
a way fo me to know that a GList has changes I can't cache a tail pointer.
Especially in that case where gtk+ (not me) created the list.
(Yet no one seems to understand this point.) Thus it is not
expandable. I have long argued that there should be a g_container
class which can point to a List, SList, Queue, etc and be dealt with
with the same methods. (sorry horrible side track, but it is related.)
But to answer your question, no the other boxed types can't have
a destroy function, because they are all leaf classes with no
hooks or ways to derive. Thus it would be pointless for them to have
such a thing.
My closure code on the other hand was meant to be derived
as well as used. This is because I realize that I don't have all the
answers and new uses may come up. Therefore, by allowing
deriving you shift future developments to the future rather than
make something perfect now. I see open types as a good feature
even for something as simple as the exception code.
> > > Requiring return codes is somewhat gross; it often leads to an
> > > unnatural API (for example gconf_get_bool() couldn't return a bool).
> >
> > I can agree with this. However, I don't see the requirement as
> > being there. The exception checking is outside the error code.
> > We are really just talking about extra return info just like I
> > did in libsigc++ for the return code ignore.
>
> 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?
> One of the big problems with return codes is that they are
> inconsistent (NULL, boolean, -1) and thus annoying to handle in
> language bindings. :-)
Then you shouldn't be dealing with C. ;-) The whole method of
dealing with returns in most system level C functions is to always
use the return code as the status and place the thing being returned
in the parameter list if the return is more complicated than yes/no.
Unfortunately, we all just ignore this and thus try to use the return
as a value and have it error check too. Thus we move the error
code arround to whatever isn't valid like 0, -1, 1, 0xFF, 2, etc.
[signals]
> No signal right now has an error object in the signature, and none
> ever should, because it makes a mess that doesn't make sense. It's
> impossible to handle errors for an arbitrary set of user callbacks.
> It's like having a Java method that throws every possible exception.
Well, a lot of things are signals in gtk+. If no signal can
have exceptions that those things that have acceptions must become
final implementations. Final implementations are inflexible (unless
this is what you are really aiming for.)
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."
> You can't use a function that raises an exception as a signal handler
> because you have to check the error code on a function that raises an
> exception, and there is no way to run code to do that in between
> signal handlers.
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.
>
> So you need a wrapper to check your error code. The simplest way to
> check the error code is to always ignore it:
>
> void my_callback(object, blah blah)
> {
> raises_exception(blah blah, NULL);
> }
>
> already you often need such a wrapper, except for the occasional
> coincidence when a stock function happens to "match".
There is a lot of gtk+ code that uses the occasional coincidence
to leverage. Just to name a common one look athe the was delete
event is connected with quit so that the data callback fills the
event slot in many of the example codes.
> > (Of course I am not really sure where the exception code is really
> > being used.
> >
>
> Typically for a small well-defined function, like
> gconf_get("/some/setting", &exc)
> or
> pango_convert_string(str, &exc)
> or
> gdk_pixbuf_load_file(filename, &exc)
>
> where you have a limited set of errors that are immediately
> interesting if they are interesting at all.
How many of those could have defined a sane behavior without
exceptions or could have stored the error code internally?
I know that for our wrapper, we defined a bad load as creating
a broken link icon and that can be checked afterward. It
is neither fatal nor will is cause segfaults later because
it enforces invarients. (after the load there will be something
there that can be displayed.)
buf = gdk_pixbuf_load_file(filename); /* guessing use from above */
if (buf&&gdk_pixbuf_is_error(buf))
{
g_print( gdk_pixbug_error_str() );
}
This is better because I can check the error outside rather than
having to handle it imediately (like during the scope of the immediate
calling function.)
[...]
> > > An alternative approach is to set a global error object instead of
> > > ignoring errors whenever the user passes NULL for the GException**
> > > argument. Then you can basically have both APIs. I'm not sure if the
> > > functionality duplication is worth it.
> >
> > This still leaves the problem of do we make a system where the
> > signal system is dealing with exceptions. They are very
> > problematic to add arguments.
> >
>
> I can't think of a single case where a signal emission should raise an
> exception. A signal emission is supposed to _notify_ you of a
> change. How can you fail to notify someone of a change? There is no
> error that makes any sense.
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 a signal emission has an error, it means one of the _handlers_
> failed. But _by definition_ the signal emitter doesn't know anything
> about the handlers, that is the _whole point of signals_. It makes
> zero sense for the signal emitter to handle these errors; the handlers
> should be handling their own errors.
>
> I would be interested to here a concrete example of this
> signal-emission-causes-error stuff. Otherwise it is just a theoretical
> concern overcomplicating the real-world cases.
I choose gtk_container_add() (okay maybe not the best choice, but
the only one I can come up with after midnight.
[...]
> Oh come on, you just add a new m4 macro for the has-exception
> case.
>
> I'm confident that all the binding authors have the skills to machine
> generate the cpp_wrapper_for_func() example I gave earlier.
>
> I know for a fact that this is easy in at least the Guile and Sugar
> cases because I've tried them, and I strongly suspect it works easily
> in gnome-python as well, and I think the code at the top of this mail
> would work fine for Gtk-- in conjunction with a new m4 macro.
Actually the m4 macros change types not their order. I would
have to make some serious lex/yacc changes to accept missing data
arguments. (Cactus tried this to make a closure of gnome callbacks
and gave up as it was a major rewriting.) Okay, yes I am a
skilled coder and can do it if necessary.
[...]
> So: perhaps we shouldn't call it GException. We are not trying to do
> some kooky exception emulation. We are just trying to report error
> information, as the UNIX API does.
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.
>
> The primary reasons for a GError object rather than UNIX-style is that
> we want better messages than strerror(), we want extensibility (new
> error types, basically unlimited errno values), and we want thread
> safety.
>
> Let's not think of this as an attempt to do exceptions in C.
>
> Complexity is death here, I promise. This is especially important
> because error-handling codepaths are rarely tested.
>
> KISS.
KISS is an threadsafe int and a array of strings. man errno :-)
>You want to a) know the operation failed b) have a useful error
> message and c) be able to programmatically respond to certain kinds of
> error. It has to be threadsafe. Any other features are just complexity IMO.
>
> Bad features:
> - exceptions that are just warnings. There's nothing to do with these
> except spew them to stdout or a dialog. If users really care
> they should be fatal, not warnings. If users don't care you
> shouldn't spew them. ergo they are useless.
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!
> - flexibility is fairly bad. You need conventions that are enforced
> as much as possible, or people break the invariants that keep the
> system working. For example they try to make signals have
> exceptions or they allow exception-pileup that creates
> post-functional ambiguous fatality. ;-)
> Excessive flexibility also increases the amount of special cases
> after every error check.
Well, derivably cost nothing. A function pointer which you
call to delete the resources which only gets used for
special cases. Thus rather that always calling a fixed function
we will just call the one supplied.
>
> The following should always work:
> if (error_occurred)
> popup_error_dialog(get_error_string());
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. :-)
> If you are planning to be more complex than either of those then
> you have me really scared as hell. :-)
Well, if is was safe and understood through out the library I
would use it to so that I can throw from C++, back through a
C function which realizes it is bad but can't handle it, back into
the C++ layer where I can make it into a real exception. JUST KIDDING!
--Karl
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]