gnu-arch-users
[Top][All Lists]
Advanced

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

Re: GError (Re: [Gnu-arch-users] "librifying" libarch)


From: Tom Lord
Subject: Re: GError (Re: [Gnu-arch-users] "librifying" libarch)
Date: Mon, 20 Oct 2003 18:51:44 -0700 (PDT)



    > From: Colin Walters <address@hidden>

    > > I don't know what you mean by this "which layer" stuff.   The GError
    > > documentation talks about domain indicating a "module" and I don't
    > > know that they mean either.

    > A module is a component of a software system.  They may be layered, or
    > they may not be.

Some languages have some kind of construct for defining an official
"module".  C doesn't.  Even in languages that have modules, in
informal discussion, a "component" or "layer" may be one module,
multiple modules, or parts of multiple modules.  The idea of
"component" or "layer" may _change_ depending on the context of the
discussion -- two lines of source code might be part of the same
component from one point of view, and parts of different components
from another point of view.

So, if you tell me to identify error codes with some "module" I'm not
at all clear on why'd I want to do that or what it might mean.  Does
that mean that only the code in that module (if the language has
official modules) can generate that error?  Or what?   Gah!


    > > I'm horrified to think that if I have a layered software system with
    > > layers: [...]
    > > that layer A can get back an error that is programmatically labeled as
    > > coming from layer C and try to handle it on that basis.   That's an
    > > abstraction violation:  A isn't supposed to know if or how B uses C.

    > Not necessarily.  For example, suppose C is a sort of filesystem
    > abstraction layer.  B is some sort of file parser or something.  You may
    > want to handle the cases where C fails differently from where B fails.
    > For example, if C fails, you may want to ask the user to check their
    > network connection.  If B fails, you might ask the user if they want to
    > try recovering the file.

In cases like that, B is defined has returning any of a set of
possible values, some of which it might just be forwarding from C.
But A shouldn't know they're coming from C.   As far as A is
concerned, it's an error from B.

The point is this:  Am I ever _really_ going to want to write code
like:

        if (my_gerror->domain == C_domain)
          {
            ....
          }


?


If not, then the whole domain/code split seems completely gratuitous.
I can't see why the interface to GError should expose it.   It should
be an implementation detail, at best.



    > In a language like Java, this would be the equivalent of:
    > 
    > try {
    >   com.example.b.doFoo(...);
    > } except (com.example.b.InvalidFileException e) {
    >   ...
    > } except (com.example.c.NetworkResetException e) {
    >   ...
    > }
    > And that makes a lot of sense from an engineering standpoint.

Oh, hush now.   Let's sort this out.

Disclaimer:  let's suppose that code snippet comes not from some
general purpose library but from some tiny app.   We don't care too
much about abstraction boundaries in the tiny app because, hey, it's
tiny!   In that case, sure, that code is good engineering.

But onto the criticism:

Did the author of com.example.b tell you that his method might return
a com.example.c.NetworkResetException?  Two possible answers, No and
Yes:

No: Oops -- your sample code almost certainly contains an abstraction
    violation.   Your code "knows" that b uses c but, really, that's a
    secret.  It's bogus engineering.  The author of b failed to
    anticipate this error, and that's a bug in his code.  [*]


Yes: Well, then the author of com.example.b screwed up.  Because in
     truth, he only wants to forward up to his callers the
     com.example.c.* exceptions from _certain_calls_ in his code.  And
     in forwarding them up, he really means "Uh, b returns this same
     kind of error."  He might later add additional calls to C that
     _shouldn't_ forward up in the same way.  So, really, b should be
     catching the error com.example.c.NetworkResetException and
     turning it into com.example.b.NetworkResetException. [**]


[*] If B didn't know he was calling C -- if the object of class C that
    caused the exception was some object handed down to B by A, then 
    passing the C exception from B and handling it in A is
    reasonable.

[**] I'm assuming that B is not an ancestor class of C.


The other thing to notice here is you didn't write:

        except (com.example.c.* e)

where * is a wildcard.   In other words, you didn't do anything
comparable to:

        if (my_gerror->domain == C_domain)

and that's my primary point: the `domain' foo is a wart on the GError
interface.


    > > GError is a sort-of-ok debugging aid, but there are plenty of
    > > altnerative ways to debug things that don't require ->domain and
    > > ->code.=20

    > The usage I gave above is the primary one, but debugging is also nicer.=20
    > For example, when you print an error you can also print the module it
    > comes from.  I do this in Rhythmbox, so you might see e.g.:

    > ** Gtk-WARNING: Foo is on crack
    > ** RhythmDB-ERROR: Database is corrupted!

For one thing, you can always just grep the source for a particular
error message and go from there.


    > >            Heck, there's a much simpler solution that's just=20
    > >            as good as domain+code:

    > >                 typedef char * arch_error_t;

    > You couldn't do what I'm talking about above with this approach.

And I wouldn't want to :-)

-t





reply via email to

[Prev in Thread] Current Thread [Next in Thread]