Instance private data



This is an idea that I've been playing around with for
a while ... now that we've frozen the GTK+ ABI for the
next couple of years, we frequently need ways to store
new private data with an object. Traditional ways to
do this are:

 - Object data
 - Allocate a separate 'priv' structure and point to
   it from the main structure.

But with a very small GObject extension, there is 
something we can do that is considerably cleaner and
more efficient; allocate private blocks after the
main structure. So, schematically:

 +-----MyWidget---+
 |+---GtkWidget--+|
 ||+---GObject--+||
 |||            |||
 ||+------------+||
 |+--------------+|
 +----------------+
 |GtkWidgetPrivate|
 +----------------+
 |MyWidgetPrivate |
 +----------------+

Note that the offset of each private structure from the end
of the public instance structure is fixed, but the size of the
instance structure varies on the parent class and subclass.

the way that this is used:

 #define GTK_WINDOW_PRIVATE(window) G_OBJECT_GET_PRIVATE (GtkWindowPrivate, window, window_private_offset)

 static gsize window_private_offset = 0;

In class_init():

   window_private_offset = g_type_add_private (G_OBJECT_CLASS_TYPE (klass),
                                               sizeof (GtkWindowPrivate));

Notes:

 a) It's not clear to me whether the standard macro name should be
    GTK_WINDOW_PRIVATE() or GTK_WINDOW_GET_PRIVATE(); the first
    is shorter, but it might look like you could then cast
    back to GTK_WINDOW().

 b) The split where we have g_type_add_private() but 
    G_OBJECT_GET_PRIVATE() is because gtype.c neds to be the party
    allocating the private structures, but G_OBJECT_GET_PRIVATE()
    is defined as:

    #define G_OBJECT_GET_PRIVATE(private_type, object, offset)      \
     ((private_type *)(((guchar *) (object)) +                      \
                       G_OBJECT_GET_CLASS (object)->private_base +  \
                       (offset)))

    -- It uses a free slot in GObjectClass to store the size of the
    instance. We didn't put any padding into GTypeClass. I don't
    think this split is very confusing, so it doesn't bother me
    much [and to get pedantic, you can even have classed but not 
    instantiatable types, so it might not make sense in GTypeClass]

    Since the instance size is in the GTypeNode, another approach
    would be to use a function call to retrieve this size.

 c) A small downside to the approach I've taken here is that 
    you get a static variable and thus a relocation for each
    type. (We already use one for the signal array, one for
    the type info, one for the type ID, so this isn't anything
    new.) An alternate approach would be to store the offset
    in the class structure for the individual type (e.g.
    GtkWidgetClass). But would increase complexity a bit,
    and use up some of the padding we reserved; so I don't
    expect it is worthwhile.

 d) As long as people are required to call g_type_add_private() in their
    class_init(), it should work fine for dynamically loaded
    types. My patch doesn't try to catch calling g_type_add_private()
    at the wrong time, but I really don't imagine that people will
    try to call it elsewhere.

Anyways - I think this addition makes a lot of sense -- the
text above is longer than the code added to GObject, and it
improves both efficiency and simplicity for storing private
fields. I'd be interested in getting people's reactions.

Regards,
                                        Owen

? gobject/closure.diff
Index: gobject/gobject.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gobject.c,v
retrieving revision 1.56
diff -u -p -r1.56 gobject.c
--- gobject/gobject.c	18 Dec 2002 23:46:18 -0000	1.56
+++ gobject/gobject.c	25 Dec 2002 21:29:00 -0000
@@ -192,11 +192,15 @@ static void
 g_object_base_class_init (GObjectClass *class)
 {
   GObjectClass *pclass = g_type_class_peek_parent (class);
+  GTypeQuery query;
 
   /* reset instance specific fields and methods that don't get inherited */
   class->construct_properties = pclass ? g_slist_copy (pclass->construct_properties) : NULL;
   class->get_property = NULL;
   class->set_property = NULL;
+
+  g_type_query (G_TYPE_FROM_CLASS (class), &query);
+  class->private_base = query.instance_size;
 }
 
 static void
Index: gobject/gobject.h
===================================================================
RCS file: /cvs/gnome/glib/gobject/gobject.h,v
retrieving revision 1.26
diff -u -p -r1.26 gobject.h
--- gobject/gobject.h	21 Mar 2002 00:34:04 -0000	1.26
+++ gobject/gobject.h	25 Dec 2002 21:29:00 -0000
@@ -44,6 +44,10 @@ G_BEGIN_DECLS
 #define G_OBJECT_CLASS_NAME(class)  (g_type_name (G_OBJECT_CLASS_TYPE (class)))
 #define G_VALUE_HOLDS_OBJECT(value) (G_TYPE_CHECK_VALUE_TYPE ((value), G_TYPE_OBJECT))
 
+#define G_OBJECT_GET_PRIVATE(private_type, object, offset)	\
+ ((private_type *)(((guchar *) (object)) +			\
+	           G_OBJECT_GET_CLASS (object)->private_base +	\
+	           (offset)))
 
 /* --- typedefs & structures --- */
 typedef struct _GObject                  GObject;
@@ -98,8 +102,11 @@ struct  _GObjectClass
   /* signals */
   void	     (*notify)			(GObject	*object,
 					 GParamSpec	*pspec);
+
+  gsize      private_base;
+  
   /* padding */
-  gpointer	pdummy[8];
+  gpointer	pdummy[7];
 };
 struct _GObjectConstructParam
 {
Index: gobject/gtype.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gtype.c,v
retrieving revision 1.58
diff -u -p -r1.58 gtype.c
--- gobject/gtype.c	18 Dec 2002 18:16:07 -0000	1.58
+++ gobject/gtype.c	25 Dec 2002 21:29:02 -0000
@@ -229,6 +229,7 @@ struct _InstanceData
   gconstpointer      class_data;
   gpointer           class;
   guint16            instance_size;
+  guint16            private_size;
   guint16            n_preallocs;
   GInstanceInitFunc  instance_init;
   GMemChunk        *mem_chunk;
@@ -909,6 +910,13 @@ type_data_make_W (TypeNode              
       data->instance.class_data = info->class_data;
       data->instance.class = NULL;
       data->instance.instance_size = info->instance_size;
+      if (NODE_PARENT_TYPE (node))
+	{
+	  TypeNode *pnode = lookup_type_node_I (NODE_PARENT_TYPE (node));
+	  data->instance.private_size = pnode->data->instance.private_size;
+	}
+      else
+	data->instance.private_size = 0;
 #ifdef	DISABLE_MEM_POOLS
       data->instance.n_preallocs = 0;
 #else	/* !DISABLE_MEM_POOLS */
@@ -1394,14 +1402,14 @@ g_type_create_instance (GType type)
       if (!node->data->instance.mem_chunk)
 	node->data->instance.mem_chunk = g_mem_chunk_new (NODE_NAME (node),
 							  node->data->instance.instance_size,
-							  (node->data->instance.instance_size *
+							  ((node->data->instance.instance_size + node->data->instance.private_size) *
 							   node->data->instance.n_preallocs),
 							  G_ALLOC_AND_FREE);
       instance = g_chunk_new0 (GTypeInstance, node->data->instance.mem_chunk);
       G_WRITE_UNLOCK (&type_rw_lock);
     }
   else
-    instance = g_malloc0 (node->data->instance.instance_size);	/* fine without read lock */
+    instance = g_malloc0 (node->data->instance.instance_size + node->data->instance.private_size);	/* fine without read lock */
   for (i = node->n_supers; i > 0; i--)
     {
       TypeNode *pnode;
@@ -1447,7 +1455,7 @@ g_type_free_instance (GTypeInstance *ins
   
   instance->g_class = NULL;
 #ifdef G_ENABLE_DEBUG  
-  memset (instance, 0xaa, node->data->instance.instance_size);	/* debugging hack */
+  memset (instance, 0xaa, node->data->instance.instance_size + node->data->instance.private_size);	/* debugging hack */
 #endif  
   if (node->data->instance.n_preallocs)
     {
@@ -3113,4 +3121,69 @@ void 
 g_type_init (void)
 {
   g_type_init_with_debug_flags (0);
+}
+
+/**
+ * g_type_add_private:
+ * @instance_type: an instantiatable type
+ * @private_size: size of private structure.
+ * 
+ * Registers a private structure for a instantiatable type;
+ * when an object is allocated, the private structures for
+ * the type and and all of its parent types are allocated
+ * sequentially in the same memory block as the public
+ * structures. This function should be called in the
+ * type's class_init() function. The return value can be
+ * used subsequently to retrieve the private structure.
+ * 
+ * Return value: the offset to add to the object location,
+ *   along with the size of the public instance, to get
+ *   the private structure. See the G_OBJECT_GET_PRIVATE()
+ *   macro for the normal way that this offset is used.
+ **/
+gsize
+g_type_add_private (GType instance_type,
+		    gsize private_size)
+{
+  TypeNode *node = lookup_type_node_I (instance_type);
+  gsize result;
+
+  if (!node || !node->is_instantiatable)
+    {
+      g_warning ("cannot add private field to non-instantiatable type '%s'",
+		 type_descriptive_name_I (instance_type));
+      return 0;
+    }
+
+  G_WRITE_LOCK (&type_rw_lock);
+
+  if (node->data && node->data->common.ref_count > 0)
+    {
+      result = node->data->instance.private_size;
+      
+      /* The 2*sizeof(size_t) alignment here is borrowed from
+       * GNU libc, so it should be good most everywhere.
+       * It is more conservative than is needed on some 64-bit
+       * platforms, but ia64 does require a 16-bit alignment.
+       * The SIMD extensions for x86 and ppc32 would want a
+       * larger alignment than this, but we don't need to
+       * do better than malloc.
+       */
+#define STRUCT_ALIGNMENT (2 * sizeof (gsize))
+      result = (result + (STRUCT_ALIGNMENT - 1)) & -STRUCT_ALIGNMENT;
+#undef STRUCT_ALIGNMENT      
+
+      node->data->instance.private_size = result + private_size;
+    }
+  else
+    {
+      g_warning ("g_type_add_private() called on unreferenced type '%s'\n"
+		 "should be called in class_init() function.",
+		 type_descriptive_name_I (instance_type));
+      result = 0;
+    }
+  
+  G_WRITE_UNLOCK (&type_rw_lock);
+
+  return result;
 }
Index: gobject/gtype.h
===================================================================
RCS file: /cvs/gnome/glib/gobject/gtype.h,v
retrieving revision 1.47
diff -u -p -r1.47 gtype.h
--- gobject/gtype.h	3 Dec 2002 23:54:54 -0000	1.47
+++ gobject/gtype.h	25 Dec 2002 21:29:02 -0000
@@ -297,7 +297,8 @@ void  g_type_interface_add_prerequisite 
 					 GType			     prerequisite_type);
 GType *g_type_interface_prerequisites   (GType                       interface_type,
 					 guint                       *n_prerequisites);
-
+gsize g_type_add_private                (GType                       instance_type,
+					 gsize                       private_size);
 
 /* --- protected (for fundamental type implementations) --- */
 GTypePlugin*	 g_type_get_plugin		(GType		     type);
Index: gtk/gtkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v
retrieving revision 1.237
diff -u -p -r1.237 gtkwindow.c
--- gtk/gtkwindow.c	16 Dec 2002 00:05:21 -0000	1.237
+++ gtk/gtkwindow.c	25 Dec 2002 21:29:39 -0000
@@ -154,12 +154,13 @@ struct _GtkWindowPrivate
   guint skips_pager : 1;
 };
 
+#define GTK_WINDOW_PRIVATE(window) G_OBJECT_GET_PRIVATE (GtkWindowPrivate, window, window_private_offset)
+
 static void gtk_window_class_init         (GtkWindowClass    *klass);
 static void gtk_window_init               (GtkWindow         *window);
 static void gtk_window_dispose            (GObject           *object);
 static void gtk_window_destroy            (GtkObject         *object);
 static void gtk_window_finalize           (GObject           *object);
-static void gtk_window_private_finalize   (GtkWindowPrivate  *priv);
 static void gtk_window_show               (GtkWidget         *widget);
 static void gtk_window_hide               (GtkWidget         *widget);
 static void gtk_window_map                (GtkWidget         *widget);
@@ -260,6 +261,7 @@ static void        gtk_window_free_key_h
 static GSList      *toplevel_list = NULL;
 static GHashTable  *mnemonic_hash_table = NULL;
 static GtkBinClass *parent_class = NULL;
+static gsize        window_private_offset = 0;
 static guint        window_signals[LAST_SIGNAL] = { 0 };
 static GList       *default_icon_list = NULL;
 static guint        default_icon_serial = 0;
@@ -305,33 +307,6 @@ mnemonic_equal (gconstpointer a, gconstp
     (ka->keyval == kb->keyval);
 }
 
-GtkWindowPrivate*
-gtk_window_get_private (GtkWindow *window)
-{
-  GtkWindowPrivate *private;
-  static GQuark private_quark = 0;
-
-  if (!private_quark)
-    private_quark = g_quark_from_static_string ("gtk-window-private");
-
-  private = g_object_get_qdata (G_OBJECT (window), private_quark);
-
-  if (!private)
-    {
-      private = g_new0 (GtkWindowPrivate, 1);
-
-      private->fullscreen_initially = FALSE;
-      private->skips_pager = FALSE;
-      private->skips_taskbar = FALSE;
-      
-      g_object_set_qdata_full (G_OBJECT (window), private_quark,
-			       private,
-                               (GDestroyNotify) gtk_window_private_finalize);
-    }
-
-  return private;
-}
-
 GType
 gtk_window_get_type (void)
 {
@@ -409,6 +384,9 @@ gtk_window_class_init (GtkWindowClass *k
   
   parent_class = g_type_class_peek_parent (klass);
 
+  window_private_offset = g_type_add_private (G_OBJECT_CLASS_TYPE (klass),
+					      sizeof (GtkWindowPrivate));
+
   mnemonic_hash_table = g_hash_table_new (mnemonic_hash, mnemonic_equal);
 
   gobject_class->dispose = gtk_window_dispose;
@@ -1940,7 +1918,7 @@ gtk_window_set_skip_taskbar_hint (GtkWin
 
   g_return_if_fail (GTK_IS_WINDOW (window));
   
-  priv = gtk_window_get_private (window);
+  priv = GTK_WINDOW_PRIVATE (window);
 
   setting = setting != FALSE;
 
@@ -1971,7 +1949,7 @@ gtk_window_get_skip_taskbar_hint (GtkWin
 
   g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
   
-  priv = gtk_window_get_private (window);
+  priv = GTK_WINDOW_PRIVATE (window);
 
   return priv->skips_taskbar;
 }
@@ -1997,7 +1975,7 @@ gtk_window_set_skip_pager_hint (GtkWindo
 
   g_return_if_fail (GTK_IS_WINDOW (window));
   
-  priv = gtk_window_get_private (window);
+  priv = GTK_WINDOW_PRIVATE (window);
 
   setting = setting != FALSE;
 
@@ -2028,7 +2006,7 @@ gtk_window_get_skip_pager_hint (GtkWindo
 
   g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE);
   
-  priv = gtk_window_get_private (window);
+  priv = GTK_WINDOW_PRIVATE (window);
 
   return priv->skips_pager;
 }
@@ -3413,13 +3391,6 @@ gtk_window_mnemonic_hash_remove (gpointe
 }
 
 static void
-gtk_window_private_finalize (GtkWindowPrivate  *priv)
-{
-  
-  g_free (priv);
-}
-
-static void
 gtk_window_finalize (GObject *object)
 {
   GtkWindow *window = GTK_WINDOW (object);
@@ -3556,7 +3527,7 @@ gtk_window_map (GtkWidget *widget)
   GdkWindow *toplevel;
   GtkWindowPrivate *priv;
 
-  priv = gtk_window_get_private (window);
+  priv = GTK_WINDOW_PRIVATE (window);
   
   GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
 
@@ -5798,7 +5769,7 @@ gtk_window_fullscreen (GtkWindow *window
   g_return_if_fail (GTK_IS_WINDOW (window));
 
   widget = GTK_WIDGET (window);
-  priv = gtk_window_get_private (window);
+  priv = GTK_WINDOW_PRIVATE (window);
   
   priv->fullscreen_initially = TRUE;
 
@@ -5838,7 +5809,7 @@ gtk_window_unfullscreen (GtkWindow *wind
   g_return_if_fail (GTK_IS_WINDOW (window));
 
   widget = GTK_WIDGET (window);
-  priv = gtk_window_get_private (window);
+  priv = GTK_WINDOW_PRIVATE (window);
   
   priv->fullscreen_initially = FALSE;
 


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