Instance private data
- From: Owen Taylor <otaylor redhat com>
- To: gtk-devel-list gnome org
- Subject: Instance private data
- Date: Wed, 25 Dec 2002 17:05:09 -0500 (EST)
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]