Re: Exporting the Gtk+ object system to interpreters
- From: Tim Janik <timj gtk org>
- To: gtk-devel-list redhat com
- Subject: Re: Exporting the Gtk+ object system to interpreters
- Date: Sun, 13 Dec 1998 19:38:45 +0100 (CET)
On 13 Dec 1998, Marius Vollmer wrote:
> Tim Janik <timj@gtk.org> writes:
>
> > On 7 Dec 1998, Marius Vollmer wrote:
> >
> > > I'm currently hacking (rather unfocused) on exporting the Gtk+ object
> > > system to Guile Scheme. [...]
> >
> > i've added a gtk_type_query() interface (similar to gtk_signal_query)
> > as you may have noticed, please tell me if that fullfills your needs.
>
> In principle yes. I don't understand why it is `better' than my
> original gtk_type_get_info, but I don't have to.
GtkTypeQuery does not expose internal private data of the type system,
and in the future it can be extended as desired, e.g.
struct _GtkTypeQuery
{
GtkType type;
const gchar *type_name;
guint object_size;
guint class_size;
+ guint n_children_types;
};
> [ I was a little bit ticked off when I noticed that you had replaced
> my perfectly working gtk_type_get_info with gtk_type_query that did
> *not* work. I have calmed down since. Sorry, if I said something
> offensive.
> ]
what do you mean by gtk_type_query "did *not*" work?
> > hm, there's actually a bunch of things that needs to be adressed to
> > get the signaling behaviour you require.
> > 1) you need to create signals that don't provide normal marshallers
> > at all (i.e. the default handlers and possibly connected handlers
> > will always be invoked with unmarshalled parameters: guint n_params,
> > GtkArg *params, etc).
>
> Yes.
>
> > 2) you need a mechanism to specify a default-handler with gpointer *data
> > arg that is not retrived as a function pointer through the normal class
> > offset, and which gets invoked with unmarshalled parameters.
>
> Yes, this would be the `full' interface for callbacks. We already
> have gtk_signal_connect_full, and now we need something like
> gtk_signal_set_class_function_full.
please read the C comments below more carefully. default handlers need to be
setup in class_init() functions and from then on are considered static data.
we shouldn't break that convention, not even for interpreter bindings, i
couldn't even come up with a reason why you'd want to subsequently
change default handlers. thus, the data argument does not need a destroy
notifier.
also, default handlers for existing C classes cannot (and actually *must not*)
be changed. thus, you may only use gtk_object_class_set_unmarshalled_handler()
for newly derived classes, from within their class initializer.
> > 3) you need a mechanism to eventually chain the default handler of the
> > parent class (e.g. if you create a new container type in scheme, you'd
> > probably want to chain the GtkContainer::add handler of the parent
> > class from within your scheme-default-handler for GtkContainer::add).
> > for GtkObject::destroy this is even mandatory.
>
> Yes.
>
> I'm going to address your posting point for point first, but at the
> end there's also a more concise exposition of what I think we should
> do. It's not much different, but still.
yep, i read through that thoroughly and i'll actually need similar variants
of a few of your proposed gtk_signal_* functions to implement the interface
i suggested, but those functions will be truely gtk-internal.
> > so if i understood your (and keneth) needs correctly, something like
> > the following should be implemented:
> >
> > typedef enum /*< flags >*/
> > {
> > GTK_RUN_FIRST = 1 << 0,
> > GTK_RUN_LAST = 1 << 1,
> > GTK_RUN_BOTH = (GTK_RUN_FIRST | GTK_RUN_LAST),
> > GTK_RUN_NO_RECURSE = 1 << 2,
> > GTK_RUN_ACTION = 1 << 3,
> > GTK_RUN_NO_HOOKS = 1 << 4,
> > + GTK_RUN_UNMARSHALLED = 1 << 5
>
> I'm not sure if we need this one. Just setting marshaller=NULL should
> be indicative enough.
for one, i want to keep marshaller=NULL as a special hint for future extensions
where we implement automated marshaller lookups upon signal creation. for
another, generic code like interface builders need to be able to figure whether
a specific signal does not provide marshalling abilities at all. this can then
be queried through GtkSignalQuery.signal_flags>K_RUN_UNMARSHALLED. besides
that, the flag is required by the signal system itself to invoke such signals
with the GtkUnmarshalledFunc prototype, i'll explain later why that prototype
is required.
> > typedef void (*GtkUnmarshalledFunc) (GtkObject *object,
> > GtkType instance_type,
> > guint signal_id,
> > guint n_params,
> > GtkArg *params,
> > gpointer data);
>
> I don't think we should introduce another type of function for this
> purpose. Gtk+ generally uses functions of type GtkCallbackMarshal to
> invoke umarshalled functions. For reference:
>
> typedef void (*GtkCallbackMarshal) (GtkObject *object,
> gpointer data,
> guint n_args,
> GtkArg *args);
>
> It would be great if we could just use this kind of function instead
> of your proposed GtkUnmarshalledFunc because the GtkCallbackMarshal is
> already there and supported by the interpreter bindings.
>
> I think we actually can just use GtkCallbackMarshal because the
> additional information that you want to pass as a service can be
> transported by whatever the interpreter bindings want to put into
> DATA. For example, the Scheme bindings have no problem channeling
> arbitrary extra data into a Scheme callback because such callbacks are
> actually `closures' that carry their complete environment.
as much as i'd like to keep the above prototype as an interface, it's
not possible to get that going with default handler chaining. you're
right in that at least guint signal_id can be eliminated, i meant that as
a convenience for you actually.
> But supporting GtkUnmarshalledFunc is certainly possible from
> guile-gtk, if not as elegant.
>
> > /* this function is just an alias for gtk_object_class_user_signal_newv()
> > * with signal_flags|=GTK_RUN_UNMARSHALLED and marshaller=NULL. it is used
> > * to create new signals for classes that are not derived in C.
> > */
> > guint gtk_object_class_unmarshalled_signal_newv (GtkObjectClass *klass,
> > const gchar *name,
> > GtkSignalRunType signal_flags,
> > GtkType return_val,
> > guint n_params,
> > GtkType *params);
>
> I would prefer not to use the GTK_RUN_UNMARSHALLED flag and just use
> gtk_object_class_user_signal_new with marshaller=NULL. But this
> function certainly does no harm.
since GTK_RUN_UNMARSHALLED signals are a very specialized type of signals,
and since i want to preserve the existing interface for existing and upcoming
code, the creation of GTK_RUN_UNMARSHALLED signals needs to be routed through
an extra interface. i originally intended to combine this with the
specification of the default handler and since this function would also call
gtk_object_class_add_signals() automatically, i picked gtk_object_class_ as
namespace prefix. after reading your plea to use GtkType instead of the object
class paramter (which cannot consistently be combined with passing GtkType
as first parameter), i alternatively suggest:
guint gtk_signal_unmarshalled_newv (const gchar *name,
GtkSignalRunType signal_flags,
GtkType object_type,
GtkType return_val,
guint nparams,
GtkType *params);
but this function would require your code to call gtk_object_class_add_signals
on its own, and thus i like the gtk_object_class_ variant better.
> > /* setting an unmarshalled default handler automatically sets the
> > /* corrsponding
> > * class function to NULL, and thus can only be done once per klass
> > * and signal.
> > * (we therefore don't need a GtkDestroyNotify function for the data arg).
> > * this can be used to override inherited default handlers
> > * in derived classes and to specify default handlers for newly created
> > * unmarshalled signals.
> > */
> > void gtk_object_class_set_unmarshalled_handler (GtkObjectClass *klass,
> > guint signal_id,
> > GtkUnmarshalledFunc default_handler,
> > gpointer data);
>
> I would prefer something compatible to the established `full' callback
> API. Something like
>
> void
> gtk_object_class_set_default_handler_full (GtkObjectClass *klass,
> guint signal_id,
> GtkSignalFunc func,
> GtkCallbackMarshal marshal,
> gpointer data,
> GtkDestroyNotify notify);
you don't give reasons for why GtkDestroyNotify is required, and as i stated
above, the gpointer data portion will never be freed at all, so i don't see
any use in GtkDestroyNotify at all. besides that, i also don't understand
what GtkSignalFunc is meant to be used for. anyways, GtkCallbackMarshal
doesn't fullfill our needs here, because the default handlers class instance
doesn't get passed through that function, and thus we'll need a prototype
like GtkUnmarshalledFunc anyways.
> > /* function to be used internally from within the signaling system,
> > * and from unmarshalled default handlers to chain the parent class'
> > * default handler. GTK_TYPE_OBJECT (object) is required to
> > * be derived from instance_type.
> > */
> > void gtk_object_invoke_default_handler (GtkObject *object,
> > GtkType instance_type,
> > guint signal_id,
> > GtkArg *params);
>
> This is fine with me.
hm, i hope you get the meaning of `instance_type' here. e.g.
for a GtkWindow, there are a bunch of GtkObject::destroy default
handlers available:
gtk_window_destroy,
gtk_bin_destroy (which equals to gtk_container_destroy),
gtk_container_destroy,
gtk_widget_destroy and finally
gtk_object_destroy.
instance_type denotes the class that the default handler shall be
picked from.
> > the default handlers will be invoked as:
> > void
> > interp_default_handler (GtkObject *object,
> > GtkType instance_type,
> > guint signal_id,
> > guint n_params,
> > GtkArg *params,
> > gpointer data)
>
> As previously, I'd like it to conform to GtkCallbackMarshal.
as previously ;), we need the instance_type for proper chaining and thus
can't use GtkCallbackMarshal.
> > {
> > /* the GtkArg params[] array conforms to the GtkType*params types, specified
> > * for gtk_object_class_unmarshalled_signal_newv(). a possible return
> > * value is stored in params[n_params].
> > */
> >
> > /* chaining of the parent class' handler (e.g. required for ::destroy) */
> > gtk_object_invoke_default_handler (object,
> > gtk_type_parent (instance_type),
> > signal_id,
> > params);
> > }
> >
> > the instance_type parameter cares about proper chaining of default handlers.
> > e.g. a scheme type SchemePrompt could derive from SchemeInput which would
> > in turn derive from GtkEntry, and both of them would override
> > GtkEditable::insert_text:
> > gtk_object_class_set_unmarshalled_handler (gtk_type_class ("SchemeInput"),
> > gtk_signal_lookup ("insert_text",
> > GTK_TYPE_EDITABLE),
> > scheme_input_default_handler_insert_text,
> > data);
>
> This would be more like
>
> gtk_object_class_set_default_handler_full (gtk_type_class ("SchemeInput"),
> gtk_signal_lookup ("insert_text",
> GTK_TYPE_EDITABLE),
> NULL,
> sgtk_callback_marshal,
> scm_proc,
> sgtk_callback_destroy);
>
> where sgtk_callback_marshal and sgtk_callback_destroy are completely
> general routines (they do exist already and are used for all other
> callbacks right now) and scm_proc would be a SCM value that refers to
> the interpreted Scheme routine that is to be used as the insert_text
> default handler. There is no way I could provide a separate C
> function for every signal that I might want to have a Scheme default
> handler.
nope, you don't need seperate C functions for every signal, remember the first
part of my mail, where i said that all default handlers that are not routed
through the class pointer get their parameters passed through an array. since
we furtherly require instance_type, you'll have to write one extra function
sgtk_unmarshalled_handler that's going to execute the scheme code equivalent
of a classes default handler, depending on the signal_id and instance_type
(that's why i originally suggested to pass signal_id in the GtkUnmarshalledFunc
prototype as well).
> For extra wrapping convenience (which might go a bit far, I'm not
> sure), it would be nice to change the interface of
> gtk_object_class_set_default_handler_full to be
>
> void
> gtk_object_class_set_default_handler_full (GtkType type,
> gchar *signal_name,
> GtkSignalFunc func,
> GtkCallbackMarshal marshal,
> gpointer data,
> GtkDestroyNotify notify);
>
> that is, use GtkType instead of a class pointer, and use the name of
> the signal instead of its id. That way, I could just add
>
> (define-func gtk_object_class_set_default_handler_full
> none
> ((type type)
> (string signal_name)
> (full-callback function)))
>
> to gtk-1.1.defs and be done with it. One can certainly provide both
> forms of the interface and it is no problem to add the latter one from
> outside of Gtk+.
we can't pass a GtkType type argument as first parameter of a
gtk_object_class_ function, read me above on gtk_signal_unmarshalled_newv()
for a GtkType variant (and the extra complexity of calling
gtk_object_class_add_signals from scheme).
> > upon emission of the signal:
> > gtk_signal_emitv (GTK_OBJECT (scheme_prompt),
> > gtk_signal_lookup ("insert_text", GTK_TYPE_EDITABLE),
> > ¶ms);
> > the call sequence will be as follows:
>
> this has all to happen in Scheme, of course. Not a single line of C
> code should need to be written to define the two new types
> SchemePrompt and SchemeInput from Scheme.
i wasn't referring to the type derivation here, that's a completely different
matter, API wise. i agree though, that you need to be able to generically
derive scheme classes without class-specific glue code.
> > void
> > scheme_prompt_default_handler_insert_text (GtkObject *object,
> > GtkType instance_type,
> > ...)
> > {
> > /* GTK_OBJECT_TYPE (object) is gtk_type_from_name ("SchemePrompt"),
> > * instance_type is gtk_type_from_name ("SchemePrompt").
> > * the next call invokes scheme_input_default_handler_insert_text()
> > */
> > gtk_object_invoke_default_handler (object,
> > gtk_type_parent (instance_type),
> > [...]
> >
> > i'm passing in the signal_id to GtkDefaultHandler, because it's required
> > to chain the parent class' default handler,
>
> I think that a default handler `knows' for what signal and class it is
> the default handler, so it also knows the signals name and the class'
> type. It is not necessary to pass in this information. Doing it
> nevertheless leads to an incompatability between the callback
> interface provided by the rest of Gtk+ and the default handler
> mechanism. I like to avoid this incompatibility.
the above mentioned sgtk_unmarshalled_handler wouldn't know what instance_type
and signal_id it is invoked for. signal_id can certainly be passed through a
gpointer data struct besides other stuff, but the instance_type definitely
needs to be provided. we are not providing a simple callback iterator here
like in gtk_container_foreach_full(), we are talking about class instance
and signal specifc default handlers that need to be able to chain a
default handler for the same signal at the parent's instance (e.g. through
a scheme function as simply as "(chain-parent)").
> > C functions that want to connect to unmarshalled signals (though
> > that behaviour is questionable, it should be taken care of) will be
> > invoked with the same signature as the default handler, i.e. that of
> > GtkUnmarshalledFunc.
>
> Again, I would strongly prefer to keep the old mechanism for this. C
> functions can already be connected to signals without a default
> marshaller via gtk_signal_connect_full. They would then be invoked as
> a GtkCallbackMarshal.
i'm not talking about marshaller-connections here like you can specify
with gtk_signal_connect_full() when passing func=NULL, i'm talking
about normal gtk_signal_connect() invokations here.
> > instance_type will always be GTK_TYPE_OBJECT
> > (object) in such cases (or we may define another
> > GtkUnmarshalledHandler type for them, having the signature of
> > GtkUnmarshalledFunc without the instance_type arg).
>
> That would be GtkCallbackMarshal.
without the instance_type and signal_id parameters, yes. i wasn't
directly aware of that parallel.
> Ok, now. Let me try to explain how I would like it to be in toto.
>
> [ Sorry for being repetive and boring, but I like to write those
> things down in the most general way to check whether the basic
> assumptions make sense.
> ]
>
> Currently, there is enough support in Gtk+ for nicely connecting
> interpreted handlers to existing signals. This is done via a
> `advanced' concept of what a callback is. A callback is not just a
> pointer to a C function that has a certain signature (that is only
> enforced by convention). Rather, it also includes a way to
> dynamically pass arguments in a type-safe way, to allow the callback
> to carry an environment with it (the DATA thing), and to get a
> notification when the callback is no longer needed by Gtk+.
>
> The signal mechanism was extended to allow for this more complicated
> notion of what a callback is but the default_handler mechanism cannot
> deal with it. This is the basic problem I want to fix. The fix
> should not interfere with the current way of handling
> default_handlers, as this way is very reasonable and efficient for the
> common case.
agreed so far, except that for default handler's data arguments, there's no
destroy notification reuired because they are static handlers that cannot
be blocked or removed again.
> So we need to have default_handlers that are not just pointers, but
> offer the full palette of options that is available to the normal
> handlers (analog to gtk_signal_connect_full). We need a way to invoke
> them in a generic way (analog to gtk_signal_emit and gtk_signal_emitv).
that's what gtk_object_invoke_default_handler() is intended for.
> Plus, we need to define new signals that have no default_marshaller
> (and no class function slot), because we can't--in general--provide
> them from interpreted languages.
that's a totaly different type of signal, compared to the ones currently
in existance. since they require special signatures and special handling
inside (and even outside) of the signal system, they need to be flagged
(as GTK_RUN_UNMARSHALLED).
> I like to extend the semantics of gtk_signal_newv and add two new
> functions, gtk_signal_set_default_handler_full and
> gtk_signal_invoke_default_handler_v. [Very, very similar to the
> things you have proposed.]
as previously, i want to keep the current semantics of gtk_signal_newv.
besides that, the default handlers are actually part of the object classes
and need to be maintained there, thus default handlers don't get set on a
per-signal basis, but rather on a per-class (and signal) basis.
> gtk_signal_newv
> ---------------
>
> guint
> gtk_signal_newv (const gchar *name,
> GtkSignalRunType signal_flags,
> GtkType object_type,
> guint function_offset,
> GtkSignalMarshaller marshaller,
> GtkType return_val,
> guint nparams,
> GtkType *params);
>
> The prototype remains the same, but it is also OK to set marshaller to
> NULL. Setting marshaller to NULL also requires function_offset to be
> 0 so that the new signal can not have a class function slot. This is
> required because one cannot invoke the default_handler via the class
> function slot without a marshaller.
function_offset=0 has already a meaning, it is an indicator for user signals
that don't provide default handlers at all. marshaller=NULL will eventually
be used in future Gtk development to automatically lookup an appropriate
marshaller, thus unmarshalled signals require an extra interface (especially
since they don't reuire the marshaller and function_offset args at all).
> The consequence of marshaller being NULL is that one can only connect
> handlers that have their own custom marshaller (via
> gtk_signal_connect_full) and that one--likewise--can only use
> default_handlers with their own custom marshaller (via
> gtk_signal_set_default_handler_full).
that's imho too much of a restriction. why shouldn't i be able to connect
to an unmarshalled signal as usual? once such signals are flagged as
GTK_RUN_UNMARSHALLED, they have a specific signature that handlers can
adhere to and thus we can still use the normal gtk_signal_connect() and
similar interfaces.
> Because there is no class function slot, default handlers for this
> signal can only be invoked via gtk_signal_invoke_default_handler_v
> (and friends) and not directly via the class function slot.
the signals do indeed have a class function slot conceptually, the slot
is just not represented by a class function pointer. user signals are the
ones that don't provide default handlers (slots) at all.
> gtk_signal_set_default_handler_full
> -----------------------------------
>
> void
> gtk_object_class_set_default_handler_full (GtkType klass,
> gchar *signal_name,
> GtkSignalFunc func,
> GtkCallbackMarshal marshal,
> gpointer data,
> GtkDestroyNotify notify);
>
> This is a new function that can be used to change (or set for the
> first time) the default handler of a signal. It works with any kind
> of signal, regardless whether it has a default marshaller or not, and
> regardless whether it has a class function slot or not.
>
> The default handler can make use of all the features of the `full'
> callback interface.
default handlers may be set only once, why would one want to change
a default handler? and what kind of assumptions is derived code allowed
to make about it's parent's default handler if they may change randomly?
because of the static nature of default handlers, GtkDestroyNotify is
not required. bsesides that, i still don't get the meaning of the
GtkSignalFunc argument, especially since we already talk about unmarshalled
signals here. even if you would want to further marshal that signal,
GtkCallbackMarshal doesn't even provide a GtkSignalFunc func argument, so
what is GtkSignalFunc to be used for?
> When the signal has a class function slot (and therefore also is
> required to have a default marshaller) and the new default handler has
> no custom marshaller (MARSHAL==NULL), then the function in the class
> function slot is simply replaced with FUNC. DATA and NOTIFY are *not*
> meaningful in this situation. [DATA can't be because the default
> handler might be invoked directly via the class function slot. NOTIFY
> could be handled, but I think it makes the implementation simpler when
> it isn't and it's not a big loss.]
> When MARSHAL is not NULL or the signal has no class function slot,
> then the full semantics apply. It does not matter whether the signal
> has a default marshaller or not in this case. The default handler is
> invoked with DATA and NOTIFY is called at the right time.
nope, default handlers specified through
gtk_object_class_set_unmarshalled_handler() will always be invoked in
unmarshalled form, i.e. with the GtkUnmarshalledFunc signature. C code
doesn't need that function at all, and we don't need marshallers here
since the whole issue is about overriding normal C marshallers with an
unmarshaled variant, and invoking signal handlers of unmarshalled signals
in an unmarshalled form.
> In this situation, the default handler can not be invoked directly via
> the class function slot, only via gtk_signal_invoke_default_handler.
> So, when the signal has such a slot, it is set to a special function
> that will trigger an error whenever it is called. It is expected that
> this error function is never called, of course. It is assumed that
> the default handler is only ever set from code that implements KLASS.
yes, and thus the class function pointer will simply be NULL (or eventually
some magic mark like ((gpointer) 42) for default handlers of
!GTK_RUN_UNMARSHALLED signals that got overridden.
this is especially required to implement overridden default handlers
without big performance hits within the signal system itself, and to preserve
the common code path in a timely fashion.
> That code can make the guarantee that there will always be a working
> class function slot. Or might choose not to make this guarantee, but
> in any case this guarantee is a static feature of a class and code
> that uses the class function slots knows whether the guarantee holds.
> All existing class effectively give this guarantee and all classes
> defined in C should to do so.
>
>
> gtk_signal_invoke_default_handler_v
> -----------------------------------
>
> void
> gtk_signal_invoke_default_handler_v_by_name (GtkObject *object,
> GtkType klass,
> gchar *signal_name,
> GtkArg *parms);
>
> This function can be used to invoke any default handler whatsoever.
> We could also provide more functions of this kind:
>
> void
> gtk_signal_invoke_default_handler_v (GtkObject *object,
> GtkType klass,
> guint signal_id,
> GtkArg *parms);
>
> void
> gtk_signal_invoke_default_handler (GtkObject *object,
> GtkType klass,
> guint signal_id,
> ...);
>
> Etc.
nope, such functions will only be required for internal use of a
gtk_object_invoke_default_handler() implementation, which in turn
may only be called to chain parent class handlers from overridden
default handlers. there's no need to additionaly bloat the signal
or object API, or to give people the impression they may invoke
default handlers at will. normally, if people want a default handler
to be invoked, that needs to go though a normal signal emission with
functions like gtk_widget_show(), or gtk_signal_emitv() for
GTK_RUN_ACTION signals. in no case should the signal emission by
bypassed and the default handlers be called directly, except when
chaining is requred (there are a few exceptions to this rule like
with class methods like GtkContainer::foreach, but that's actually
part of Gtk's internal magic ;).
> So, this is very, very, very much like what you have proposed, but I
> like it slightly better because I think it fits better into the
> existing conventions.
i think i made a fair attempt with this reply to point out our differences
and why your approach would even violate certain existing conventions.
> As a first step I would be happy to have the ability to define new
> signals that have no default marshaller, no class function slot and no
> default handler. This can be done by slightly extending
> gtk_signal_newv to allow marhsaller to be NULL (and simultanously
> requiring FUNCTION_OFFSET to be 0, too), and changing
> gtk_signal_connect to only allow handlers with custom marshallers to
> connect to those signals.
we can have that functionality pretty fast with the GTK_RUN_UNMARSHALLED
flag.
> I don't feel like implementing the more involved
> gtk_signal_set_default_handler_full stuff before 1.2. But it would be
> nice if something like my original patches (gtk_type_query and the
> extended semantics of gtk_signal_new/gtk_signal_connect_full) could be
> included.
i agree that we might want to defer the implementation of overridden
default handlers a bit.
> Anyone who has managed to read upto here is invited to Free and Open
> beer, should we ever meet.
be carefull, i'm living in germany and could actually come around some day ;)
>
> cheers,
> Marius
>
---
ciaoTJ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]