Re: GError



On 22 Jun 2000, Owen Taylor wrote:

> The unresolved items in that thread were:
> 
>  1) Should it be possible to have user-data in exceptions other than
>     the string. 
>     
>     The argument for would be that this is a feature of
>     many languages, (Java, C++, Python, etc.) and of CORBA. So it 
>     must be useful, right? And there are a few cases where it is 
>     genuinely useful. Probably the most common is location information
>     for a parse error to enable an editor to jump to the spot.

hum, so far in parser code i've written, the position information was readily
available through the parser since after error aborts. and more interestingly,
quality parsing state report code needs more powerfull interfaces, e.g. a
resizable string to catch parser warnings as well.
imagine finding an error in C code if gcc would only report abort conditions.
simple exercise: let someone put an opening brace (or comma) at the beginning
of some random header file, then try compiling code and find the error ignoring
warnings ;)
the point i'm trying to make is that for parsers GError is probably not powerfull
enough (and shouldn't be), you need to take aditional measures to give valuable
responses to the user.
GError should just be a convenient interface for _functions_ to return state
to callers, having a descriptive string attached to them should be enough
if the condition actually needs to be exposed to the user.


>     The argument against, is that people almost never use the feature
>     except to add explanatory strings:
> 
>     - C++'s <stdexcept> has no data members other than explanatory strings.
>     - Python standard exceptions have line number for Syntax Error,
>       and errno for EnvironmentError, and no others.
>     - In a highly scientific survey, almost all exceptions in the Java
>       class libraries have no constructor arguments other than a string.
> 
>     The fact that it _is_ even occasionally useful may be a fairly compelling 
>     to add the feature - after all, trying to parse an error string
>     is a poor fallback.
> 
>     Given that we want the ability to set user data, there are basically two
>     approaches:
> 
>      a) Inheritance:
> 
>       struct _GError 
>       {
>         GQuark module;
>         gint error;
>         gchar *str;
>         GDestroyNotify notify;
>       };
>     
>       struct _FooParseError 
>       {
>         Error err;
>         int lineno;
>       }
> 
>       The Foo library is responsible for allocating a full FooParseError structure,
>       filling in the fields, and setting notify appropriately.
> 
>       int foo_parse_error_get_lineno (Error *err);


at this point i wonder whether you at all considered using the type system
here, extending a bit on havoc's recent approach (imho somewhat bloated, but
spinnig this for the pure fun), provide convenient registration functions:

the Foo module wants to throw OutOfResource (OFR) errors which supply
additional information as to what resource and when this happened:

typedef struct
{
  GError    base;
  gchar    *resource;
  GTimeVal  stamp;
} FooErrorOFR;

/* register this error type once */
GType foo_ofr = g_error_register_new ("FooOutOfResource",
                                      sizeof (FooErrorOFR),
                                      foo_ofr_error_copy,
                                      foo_ofr_error_free);

GError*
foo_error_ofr (gint code, gchar *resource, gchar *format, ...)
{
  FoorErrorOFR *error;
  va_list var_args;
  
  va_start (var_args, format);
  error = g_error_new_valist (foo_ofr, code, va_list);
  va_end (var_args);
  
  error->resource = g_strdup (resource);
  g_get_current_time (&error->stamp);
  
  return G_ERROR (error);
}

error throwing code example:
{
  gint fd = open ("/tmp/myspool", O_CREAT);
  
  if (fd < 0 && (errno == EMFILE || errno == ENFILE))
    return foo_error_ofr (errno, "filedescriptors",
                          "failed to open %s", "/tmp/myspool");

error handling code:
  if (g_error_is_a (G_ERROR (error), foo_ofr))
    g_print ("Stamp-%u: %s: No %s available\n",
             error->stamp, error->base->blurb, error->resource);

that may seem a bit bloated, but to be practical, all that's new here is the
one call to g_error_register_new(). the typedef struct {} FooErrorOFR; has to
be supplied if the gpointer data; or owen's GError err; embedding approach is
used, and foo_ofr_error_copy() as well as foo_ofr_error_free() already came
with havoc's latest proposal (and you'd need a foo_error_ofr() constructor for
those as well).
the advantage here is that foo_ofr_error_copy() and foo_ofr_error_free() don't
need to be supplied with every error creation call (thus doing a better job at
encapsulation, allowing multiple error constructors that behave differently),
and the error itself can be passed around in GValue structures since its
registered with the type system.

note that use of g_error_register_new() isn't mandatory, for sane programmers
that just need a descriptive string and an error code, the plain g_error()
API is fully sufficient.

>      b) user_data.
> 
>        struct _GError 
[...]
>       GError *g_error_new_with_data (GQuark module, gint module, const gchar *str, 
>                                      gpointer data, GDestroyNotify notify);
> 
>       int foo_parse_error_get_lineno (Error *err);
> 
>      a) is marginally more efficient, but given that GError is for error handling,
>      I think the increased encapsulation and simplicity of b) is worth that
>      overhead.


>   2) How are errors allocated. The simple scheme that I've discussed with Havoc
>      and I think works fine is that each module has a quark and an enumeration 
>      of errors.

[...]

> I think this setup will provide a nice standard way of reporting errors from 
> functions that can fail, and would like to see it in GLib-2.0.


there's a virtually infinitely big pool of solutions for error reporting to
choose from, just as there is a virtually infinite number of reasons for error
occourances, so this could easily lead to yet another religious (and thus
fruitless) discussion.
to take this to peak, essentially an isolated error structure with intrinsic
state information that arbitrary code portions can "send"/"throw"/"return" and
other arbitrary code portions can "receive"/"handle"/"react to" doesn't differ
that much from generic event or messaging models anymore, so why don't we just
implement a send_msg/recv_msg facility for all objects right away ;)

to avoid that, i personally prefer the simply way of simply having
GError { GQuark domain; gint code; gchar *blurb; } and drop the user
data idea alltogether which unavoidedly comes with copy/free/whack_me/whatnot
complexity.

> 
> Regards,
>                                                       Owen
> 

---
ciaoTJ





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