#107397 (Re: type_info macro generation ...)



On Fri, 19 Sep 2003, Owen Taylor wrote:

> On Fri, 2003-09-19 at 09:14, Michael Meeks wrote:
> > Hi Owen,
> >
> > 	Just poking at the GTypeInfo stuff, and I appreciate how controversial
> > eg. bonobo's "DO_TYPE_EVERYTHING" type macros are (pwrt. interfaces etc.
> > etc.).
> >
> > 	It struck me that if instead of having to do:
> >
> >      static const GTypeInfo foo_info = {
> > 	sizeof(FooClass), NULL, NULL, foo_class_init, NULL, NULL,
> > 	sizeof(Foo), 0, foo_init, NULL
> >      };
> >
> > 	or somesuch; perhaps a significant size saving could be had from a
> > macro something like:
> >
> > 	G_TYPEINFO_DECL(foo, Foo);
> >
> > 	or somesuch ? - that could shrink the get_type by a good number of
> > lines for the very common case.
>
> As far as I know, there's nothing at all controversial about the libgsf
> style of macro that defines the entire get_type() function. That is,
> everybody who has commented on it thinks it's a good idea. Still haven't
> heard from Tim.
>
> The BONOBO_BOILERPLATE style with the prototypes for class_init and init
> and the class_init trampoline to set the parent type, I think *is*
> to comprehensive. Plus, it doesn't do interfaces.
>
> But I like the libgsf style a lot.


after looking at the various boilerplate macros in CVS, the different
(sometimes project-specific) features they offer and the things that
can be (were) gotten wrong, i went for a version that allowes great
flexibility and reduces overhead up to a reasonable extend, while taking
care of as much of the mechanic error-prone tasks as possible and staying
reasonably simple.
for convenience reasons and because i don't expect any actual harm,
i took the freedom to clutter the G_DEFINE and G_IMPLEMENT namespaces
(similar to the pango variants). the result is:

/* --- GType boilerplate --- */
/* convenience macros for type implementations, which for a type GtkGadget will:
 * - prototype: static void     gtk_gadget_class_init (GtkGadgetClass *klass);
 * - prototype: static void     gtk_gadget_init       (GtkGadget      *self);
 * - define:    static gpointer parent_class = NULL;
 *   parent_class is initialized prior to calling gtk_gadget_class_init()
 * - implement: GType           gtk_gadget_get_type (void) { ... }
 * - support custom code in gtk_gadget_get_type() after the type is registered.
 *
 * macro arguments: TypeName, type_name, TYPE_PARENT, CODE
 * example: G_DEFINE_TYPE_WITH_CODE (GtkGadget, gtk_gadget, GTK_TYPE_WIDGET,
 *                                   g_print ("GtkGadget-id: %lu\n", g_define_type_id));
 */
#define G_DEFINE_TYPE(TN, t_n, T_P)                         G_DEFINE_TYPE_INTERNAL (...)
#define G_DEFINE_TYPE_WITH_CODE(TN, t_n, T_P, _C_)          G_DEFINE_TYPE_INTERNAL (...)
#define G_DEFINE_ABSTRACT_TYPE(TN, t_n, T_P)                G_DEFINE_TYPE_INTERNAL (...)
#define G_DEFINE_ABSTRACT_TYPE_WITH_CODE(TN, t_n, T_P, _C_) G_DEFINE_TYPE_INTERNAL (...)

/* convenience macro to ease interface addition in the CODE
 * section of G_DEFINE_TYPE_WITH_CODE() (this macro relies on
 * the g_define_type_id present within G_DEFINE_TYPE_WITH_CODE()).
 * usage example:
 * G_DEFINE_TYPE_WITH_CODE (GtkTreeStore, gtk_tree_store, G_TYPE_OBJECT,
 *                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
 *                                                 gtk_tree_store_tree_model_init));
 */
#define G_IMPLEMENT_INTERFACE(TYPE_IFACE, iface_init)       { \
  static const GInterfaceInfo g_implement_interface_info = { \
    (GInterfaceInitFunc) iface_init, \
  }; \
  g_type_add_interface_static (g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
}



on the pro side, a brief test object implementation is less than 20 lines:

/* --- GxkGadget --- */
typedef struct {
  GtkWidget parent_instance;
} GtkGadget;
typedef GtkWidgetClass GtkGadgetClass;
GType gtk_gadget_get_type (void);
G_DEFINE_TYPE (GtkGadget, gtk_gadget, GTK_TYPE_WIDGET);
static void
gtk_gadget_class_init (GtkGadgetClass *klass)
{
}
static void
gtk_gadget_init (GtkGadget *self)
{
}
/* --- done! --- */

and a more sophisticated version could use:

static void   gtk_gadget_tree_model_init    (GtkTreeModelIface    *iface);
static void   gtk_gadget_tree_sortable_init (GtkTreeSortableIface *iface);
G_DEFINE_TYPE_WITH_CODE (GtkGadget, gtk_gadget, GTK_TYPE_WIDGET,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
                                                gtk_gadget_tree_model_init);
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_SORTABLE,
                                                gtk_gadget_tree_sortable_init);
                         );


on the con side, though the majority of the common cases is covered,
not all possible type registration cases are supported:
1) type implementations are forced to provide class_init()
   as the class init function is prototyped within G_DEFINE_TYPE()
2) type implementations are forced to provide instance_init()
   as the instance init function is prototyped within G_DEFINE_TYPE()

i collected some data [1] from CVS to figure whether this would impose a
noticable inconvenience. it turns out that in CVS HEAD we have about 1700
uses of GTypeInfo, out of which:
90.18%	provide class_init and instance_init
 6.16%	provide only class_init
 3.64%	provide neither

so with G_DEFINE_TYPE() enforcing the implementation of instance_init()
and class_init(), the effective penalty out of a 100 cases is:
 3.64%	forces extra class_init() implementation
 9.80%	forces extra instance_init() implementation

in other words, that is one empty instance_init() prototype per saving
10 get_type()-implementations. i think that's an accaptable trade off
for avoiding class_init_function_name and instance_init_function_name
arguments for G_DEFINE_TYPE() and being able to provide the prototypes
automatically.

3) instance_init(Instance *self, InstanceClass *real_class) implementations
   that need the real instance class as argument upon initialization of super
   types are not possible with G_DEFINE_TYPE() since it prototypes the
   instance_init() function without the extra class argument. from my
   experience, code using this feature is rare enough though to justify
   implementing get_type() from scratch.
4) code using the same class_init() function to implement multiple types
   by parameterizing them via class_data are not possible with G_DEFINE_TYPE()
   either, due to prototyping class_init() without the extra class_data()
   argument, and due to not allowing class_data modification within the
   _get_type() implementation. again, this facility seems to be exploited
   rarely enough to justify get_type() implementations from scratch.
5) code not needing parent_class will get it anyway. that should be negligible
   as many objects want a parent_class pointer and the overhead of providing
   it unnecessarily is an extra pointer and one extra function call per class.
6) G_DEFINE_TYPE() can't be used multiple times in a single C file, due
   to the multiple parent_class pointers clashing. if people really are
   defining lots of small objects in a single C file, they can cook their
   own macro like this:
#define MY_DEFINE_TYPE(TypeName, type_name, TYPE_PARENT) \
        G_DEFINE_TYPE_INTERNAL (TypeName, type_name, \
                                TYPE_PARENT, 0, type_name##_parent_class, {})
   and then write code like:
   MY_DEFINE_TYPE (GtkGadget, gtk_gadget, GTK_TYPE_WIDGET);
   [...]
     GTK_WIDGET_CLASS (gtk_gadget_parent_class)->expose_event (...);


final remarks:
i've tested the code with a rough 20 objects, but i'd apprechiate
it if people could give the macros a try, especially the interface and
abstract type variants.
some of the boilerplate definition sets in the various modules also included
macros to call parent class vfunctions, setup empty signal handlers, and a
few other exotic things. i found none of them to be generally usefull
and profitable enough to go into gobject straight. if there's someone
of a different opinion, please start a seperate thread about such macros
on this list.

>
> Regards,
> 						Owen
>

[1] for those wanting to draw their own figures, drop me a line and i'll
    send you the perl script to extract GTypeInfo initialization from the
    2+GB of CVS HEAD modules, or the results file right away (ca. 90Kb).

---
ciaoTJ





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