Using By-Value Compound Getters (Re: gtk_widget_get_allocation)



On Fri, 17 Apr 2009, Cody Russell wrote:

This is rather old, but it never came up again after this so I'd like to
see what thoughts are about how to implement this in C.  It was in 2.13
but removed before 2.14 because of disagreement, but I can't find any
public record of the disagreement in gtk-devel-list.  Can anyone comment
on what was not liked, or how a better implementation could be made?

There was a lengthy #gtk+ discussion about this that could be dug out of
the archives. But I'll better summarize the original API motivation here:

  GtkAllocation gtk_widget_get_allocation (GtkWidget *widget);

Structures like GtkAllocation/GtkRequisition are very similar to (newer)
C standards constructs like struct Complex { double real, imag; }, i.e.
a fundamental compound, fully exposed to the user. Consequently they
shouldn't be sealed, but be directly used and manipulatable by the user.

An important part of what makes dealing with Gtk+ in C so cumbersome is
the variations and inconvenient definitions in our getter APIs. Many force
two lines instead of one-liners for accesing a simple value if compound
structures are involved.
This is unneccessarily so, as long as returned structs doesn't get too big,
passing by value is convenient, and acceptable performance wise (and
definitely portable C as well).
FWIW, returning a GtkAllocation is exactly as many bytes as returning
a double.

Here's an extreme case of getter inconvenience in Gdk/Gtk+:

   void        gdk_window_set_user_data   (GdkWindow     *window,
                                           gpointer       user_data);
   void        gdk_window_get_user_data   (GdkWindow     *window,
                                           gpointer      *data);

The choosen API might look most symmetric, but the getter is utterly
cumbersome to use, currently it forces us to:

   gpointer helper_variable; // declare this lines away at start of code block
   gdk_window_get_user_data (window, &helper_variable); // getter
   foo = helper_variable; // use

Instead of what could have simply been:

   foo = gdk_window_get_user_data (window); // getter, use, no helper

For a central API like Gtk+s which is very widely used, 1 or 2 additional
user code lines forced by our API choices can make a huge difference if
its needed in many places.
That is the case here, lots of code portions that currently do
  widget->allocation.{member}
could either be changed to:
  gtk_widget_get_allocation (widget).{member}
in the future, or to multiple lines of code, possibly spread out a
fair bit, because a helper variable needs to be declared.

And while returning a GtkAllocation* from gtk_widget_get_allocation()
would technically be feasible for the immediate current migration,
in the future we might want to hand out coordinates different from
the actual widget->allocation (e.g. handing out the allocation assigned
by a parent and not the possibly "adjusted" one stored in the child
like GtkSpinButton does it).
This has basically the same problem as const char*, either we hand out
a pointer to internals and in the absence of GC force ourselves into
keeping certain structure members around forever, or we hand out a
copy and force the caller into freeing (forcing 3 instead of 1 lines
of caller code).


To sum up our getter options:

a)
   /* force adding lots of helper variables into user code */
   void gtk_widget_get_allocation (GtkWidget     *widget,
                                   GtkAllocation *allocation);

b)
   /* convenient access, no garbage collection problem */
   GtkAllocation gtk_widget_get_allocation (GtkWidget *widget);

c)
   /* tie ourselves into always keeping the handed out GtkAllocation
    * allocated in the widget, which can be different from the
    * actual widget->allocation.
    */
   const GtkAllocation* gtk_widget_get_allocation (GtkWidget *widget);

d)
   /* force caller to free returned allocation, most often forces
    * an extra GtkAllocation *helper; variable.
    */
   GtkAllocation* /*freeme*/ gtk_widget_get_allocation (GtkWidget *widget);


Here, (c) is the worst as it preserves part of our current API problem
we intended to fix with GSEAL(). Now, (a) is what's often used in Gtk+
currently, but forces user code into explicitely adding helper variables.
Together with non-C99 compliance, it forces *two* simultaneous code
changes many lines *apart*.
Currently (d) is what we do for strings, most often an additional
helper variable like in (a) is needed here as well, to allow access
and freeing, e.g.
  GtkAllocation *a;
  a = gtk_widget_get_allocation (widget);
  foo = a->{member};
  g_free (a);

So, given the technical problem of (c), and then the pain and error-
proneness inflicted on users by (a) and (d), I highly prefer (b).

At first sight, it might seem a bit inefficient over the other variants,
but upon closer inspection (sizeofs) and given resonable compiler
optimizations, it's not doing more structure copies than (a) or (d),
and doesn't come with heap modification overhead like (d).
Using register returns, it could even turn out more efficient than (a)
for some optimization combinations, but apart from (d), possible speed
differences around these API choices are really negligible.
On 32bit systems (c) does lesser copying (not on 64bit), but then
has a major technical problem so can't be used anyway.

So in the interest of not making our C API users life even harder,
by forcing the writing of useless extra lines in many places and thus
creating more code that's prone to errors, it'd be best for Gtk+
to provide convenient accessors in the style of (b) in the future.

Keeping current uses just because we're used to them is going to
prevent change for the better. And introducing a new API style will
create "inconsistencies" only temporarily, fixing all getters over to
the new style and cleaning old cruft gets rid of the "inconsistency"
argument as well.

/ Cody

Thanks for picking this up again Cody.


I think I've brought up everything useful around this controversy
at this point, and sincerely hope the community will come to senses
for its own convenience around this issue.

FWIW, this is not a vendetta out of *personal* interests of mine ;)
I usually write GCC code if coding in C these days, and that allows
me to work around such legacy APIs with e.g.:

#define widget_get_allocation(w) \
  ({ GtkAllocation a; gtk_widget_get_allocation (w, &a); a; })

I'd just hope Gtk+ application writers out there didn't have to
introduce so much convenience glue.

---
ciaoTJ


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