Re: [gtk-devel-list] Re: GtkType stuff



Tim Janik <timj@gtk.org> writes:

> > GtkArgs are used to pass values to signal handlers.  Thus, as of now,
> > signal handlers can only have signatures that are expressable with the
> > current GtkArg structure.  Thus, there is no signal handler that has
> > arguments with modes that are anything but "in".  Because the GtkArg
> > structure can not express other modes.
> 
> hm, there at least have been some signal handlers like this, i.e. the
> old GtkWindow::move_resize signal (still in the 1.0.x tree):
>   gint (* move_resize) (GtkWindow *window,
>                         gint      *x,
>                         gint      *y,
>                         gint       width,
>                         gint       height);

Ah, I wasn't aware of this one, but I think there is a
misunderstanding here.  I think calling the thing `modes' was a
mistake and I will call them `flow hints' in the sequel.  C has modes
and the way I want to define flow hints is not the same as modes in
ADA, or the `var' keyword in Pascal.  The flow hints do not talk about
whether we make a new variable when calling a function, or whether we
reuse an existing one, but rather, what a function is allowed to do to
the values that get passed to it.

Flow `in' means that it is not allowed to mutate that value.  This is
different from not being allowed to change the variable (or argument)
that happens to hold this value.  It's more like the `const'
qualifier.  Actually, it's probably exactly like the `const'
qualifier.  Hmm...

There is no way to mutate an integer, for example.  You can't write
something like

    1 = 2;

Of course, you can write

    int x = 1, y = 1;
    x = 2;

but that is only changing the object that x denotes.  The variable y
is of course unaffected and still has 1 as its value.

So flow `in' is all that makes sense for integers, and for most of the
other GTK_TYPE_ types defined so far (GTK_TYPE_OBJECT, GTK_TYPE_BOXED
and GTK_TYPE_STRING being the exceptions).

But you *can* mutate an array.

    int array[1] = { 1 };
    int *x = array, *y = array;
    x[1] = 2;

This is actually modifiying the object, not merely making x denote
some other object.  Since y denotes the same object, you can observe
the mutation thru y.

The flow hints are intended to make this propagation of mutations work
efficiently even across language barriers.

In short, it makes no sense to mark a GTK_TYPE_INT as `flowing out'
because there is no way we could mutate the integer.  But we can
mutate arrays, and thus, we should be using them for outward flowing
information, exactly like C does.

> in general, "in" arguments are always a little questionable for signal

Don't you mean "out"?

> handlers, because there is 1) no mechanism to check the returned values
> for validity upon every handler invokation, and 2) it is not always clearly
> defined when such modifications will take effect, i.e. does changing a
> value that is passed by reference take effect if modified in an _after
> handler?.

Yes, agreed.
 
> since this distinction is not clearly made in C, other than passing
> the values by reference, signal handlers implemented in C only deal
> with either "in" values or with "inout" values in that sense. so the
> required distinction boils down to one bit of information, right?

Hmm, no.  The flow hints would be used to avoid unecessary conversion
operations.  A Scheme array can not be used directly as a C array, we
have to convert it first by allocating a C array of the right size and
then filling it from the Scheme array element by element.  When the
called function returns, we have to synchronize the two arrays again.
The flow hints would allow us to skip one of these conversions.  When
the flow is `in' we don't need to convert after the function has
returned; when the flow is `out' we don't need to convert before it is
called; and when it is `inout' both conversions are needed.

That's 1.5 bits, but it's of no fundemental importance whether we need
1 or 2 bits to encode the flow hints, right?

> for one thing we could, while preserving the current _GtkArg struct
> size, reduce the actuall type information to 31 bits and have
> another one, indicating "inout" behaviour:
> 
> struct _GtkArg
> {
>   guint type : 31;
>   guint is_inout : 1;
>   gchar *name;
>     
>   union {
>   [...]
>   } d;
> };
> 
> but this seems somewhat hackish, and i'm not sure that we do need
> this at all, since the destinction can already be made through the
> fundamental type anyways.

Yes, agreed.

> to carry the above mentioned on-bit-mode-information a bit further,
> what about providing:

I think the following is cleaner.  It would restrict us to 64
fundamental types, but that is still plenty.  We would have: top 24
bits: seqno, then 2 bits flow hints, last 6 bits fundamental type.

typedef enum {
  GTK_FLOW_IN = 0,
  GTK_FLOW_OUT = 1,
  GTK_FLOW_INOUT = 2
};

/* Macros
 */
#define GTK_TYPE_MAKE(parent_t, seqno)  (((seqno) << 8) | GTK_FUNDAMENTAL_TYPE (parent_t))
#define GTK_FUNDAMENTAL_TYPE(type)      ((GtkFundamentalType) ((type) & 0x3F))
#define GTK_TYPE_SEQNO(type)            ((type) & ~0xFF ? (type) >> 8 : (type) & 0x3F)
#define GTK_TYPE_FLOW(type)              (((type) & 0xC0) >> 6)
#define GTK_TYPE_SET_FLOW(type, flow)    ((type & ~0xC0) | (flow << 6))

> with this, for the above mentioned ::move_resize signal, you'd actually do:
>     gtk_signal_new ("move_resize",
>                     GTK_RUN_LAST,
>                     object_class->type,
>                     GTK_SIGNAL_OFFSET (GtkWindowClass, move_resize),
>                     gtk_window_marshal_signal_1,
>                     GTK_TYPE_BOOL, 4,
>                     GTK_REFERENCE_TYPE (GTK_TYPE_INT),
>                     GTK_REFERENCE_TYPE (GTK_TYPE_INT),

                      GTK_TYPE_SET_FLOW (gtk_type_get_fvec (GTK_TYPE_INT,
                                                            GTK_FLOW_INOUT)),

> > I think the most prominent user of GtkArg structures right now is the
> > signal system.
> 
> nope, the signaling system does actually only need the argument values
> and their type information, but GtkArgs are also used by the object
> argument system, which indeed does makes extensive use of the argument names.

Yes, i wanted to say that the signal system incurs probably the most
overhead from using the GtkArg structures.

> >  I've shortly thought about avoiding this by putting
> > some assembler magic into place that extents the va_args mechanism so
> > that you can call a function with an va_list.  Along the lines of what
> > libffi or ffcall provide, but more specialized.
> 
> peter wrote a few lines on why he had not taken this approach for the
> signaling system in the .texi files.  i think the biggest problem with
> constructing functions calls is portability. Gtk runs on a variety of
> systems currently, and without getting comprehensive access to these
> systems we can't adhock let the existing marshalling mechanism vanish,
> in favour of a global function call constructor.
> i don't know how portable libffi/ffcall are, though...

No, I don't want to remove the GtkArg mechanism, of course, just
provide a more performance optimized implementation for certain
platforms.
 
[ Declarations in header files ]

> > I think the same, but I don't want to stop at being close.  I want to
> > get there.
> 
> that's what i intended the comment-enclosed hints for, just like
> owen did for enums.

Yes, but ommitting these hints would make the system err on the
dangerous side, not on the safe side.  When someone writes a new
function and does not provide the necessary hints (because he doesn't
know or care anbout them), we get incorrect behaviour.

That is, I don't want to have the possibility for introducing bugs by
not knowing about the *.defs system.  I can live with incomplete
functionality, tho.

> i guess, a good start at advancing gtk's type system in a usefull way,
> is to solve the above mentioned pass-by-reference problem.

Do you think we are getting closer?



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