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



On 5 Sep 1998, Marius Vollmer wrote:

> 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"?

of course.

> > 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.
> 

well, ok, i got the idea. but what do we actually need the flow hints
for, then? gtk's type system doesn't know about arrays currently, and i
think you wouldn't want to do this for objects as well, right?

> 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.

trade some seqno bits for that then, so we only have 22 seqno bits available,
that still makes up more than 4 millions, which is pretty much enough for
a single program, right? ;)

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

i'd rather like these enum values to carry the bits directly, rather
than their shift count, because they are actually flags indicating certain
behaviours.

typedef enum
{
  GTK_FLOW_IN         = 1 << 8,
  GTK_FLOW_OUT        = 1 << 9,
  GTK_TYPE_FLOW_INOUT = GTK_FLOW_IN | GTK_FLOW_OUT,
  GTK_TYPE_FLAG_MASK  = 0x0300
} GtkTypeFlags;

types will be created with GTK_FLOW_IN by default of course.
 
/* Macros
 */
#define GTK_TYPE_MAKE(parent_t, seqno)    (((seqno) << 10) | GTK_FLOW_IN | GTK_FUNDAMENTAL_TYPE (parent_t))
#define GTK_TYPE_SET_FLAGS(type, flags)	  ((type) | ((flags) & GTK_TYPE_FLAG_MASK))
#define GTK_TYPE_UNSET_FLAGS(type, flags) ((type) & ~((flags) & GTK_TYPE_FLAG_MASK))
#define GTK_TYPE_FLAGS(type)              ((type) & GTK_TYPE_FLAG_MASK)
#define GTK_TYPE_FLOW_IN                  (((type) & GTK_FLOW_IN) != 0)
#define GTK_TYPE_FLOW_OUT                 (((type) & GTK_FLOW_OUT) != 0)
#define GTK_TYPE_FLOW_INOUT               ((type) & GTK_FLOW_IN && (type) & GTK_FLOW_OUT)

> /* 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))

you left out a very important part in your quotation. i originally also
rewrote the GTK_FUNDAMENTAL_TYPE() macro:

-#define GTK_FUNDAMENTAL_TYPE(type)      ((GtkFundamentalType) ((type) & 0xFF))
-#define GTK_TYPE_SEQNO(type)            ((type) > 0xFF ? (type) >> 8 : (type))
+#define GTK_FUNDAMENTAL_TYPE(type)      \
+ ((GtkFundamentalType) (((type) & GTK_REFERENCE_FLAG) ? GTK_TYPE_POINTER : (type) & 0xFF))
+#define GTK_TYPE_SEQNO(type)            \
+ (((type) & (~GTK_REFERENCE_FLAG) > 0xFF) ? ((type) & (~GTK_REFERENCE_FLAG) >> 8 : \
+   ((type) & (~GTK_REFERENCE_FLAG))

the fundamental idea was, that setting the GTK_REFERENCE_FLAG would
*change* the fundamental type to GTK_TYPE_POINTER, so that current code
still handles the passed-by-reference type accordingly.

so for the macros i propose above, something like

#define GTK_FUNDAMENTAL_TYPE(type)        ((GtkFundamentalType) ((type) & 0xFF))
#define GTK_TYPE_SEQNO(type)              ((type) & ~0xFF ? (type) >> 10 : (type) & 0xFF)

wouldn't be sufficient, but rather something like

#define GTK_FUNDAMENTAL_TYPE(type)      \
 ((GtkFundamentalType) (((type) & GTK_FLOW_OUT) ? GTK_TYPE_POINTER : (type) & 0xFF))
#define GTK_TYPE_SEQNO(type)              ((type) & ~0xFF ? (type) >> 10 : (type) & 0xFF)

some of the above GTK_FLOW_* flags needs to indicate the C language
pass-by-reference behaviour, even if you don't intend that as a meaning
for the "flow-type" concept. otherwise, we'd be pretty much forced to change
all GtkType handling code in existance.

> > 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)),

so this would become
                        GTK_TYPE_SET_FLAGS (GTK_TYPE_INT, GTK_FLOW_INOUT),
                        GTK_TYPE_SET_FLAGS (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'd be interested in hearing owen's comments on this issue, since we seem to
be stuck wrt the amount of headaches we want to cause for the intermediate gtk
programmer (occasional widget writers).

> > 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?

i hope so ;)

> 
> 

---
ciaoTJ



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