[PATCH] g_type_add_interface_check()



Here is a patch to add, basically as described in your last mail:

typedef void (*GTypeInterfaceCheckFunc) (gpointer func_data,
                                         gpointer g_iface);

void g_type_add_interface_check    (gpointer                check_data,
                                    GTypeInterfaceCheckFunc check_func);
void g_type_remove_interface_check (gpointer                check_data,
                                    GTypeInterfaceCheckFunc chec_func);

Notes:

 - I removed the gboolean return from GTypeInterfaceCheckFunc, since
   I couldn't see anything useful for GObject to do with it; if
   the check fails, GObject can't gracefully not initialize the object.

 - I removed the g_class argument from GTypeInterfaceCheckFunc, since
   it seemed simple for callers to just use iface->instance_type

 - I kept the ordering of data/func for the callback and the two functions,
   though that seems quite strange to me. It is consistent with
   g_type_add_class_cache_func()

 - I kept your names g_type_add_interface_check(), though from
   g_type_add_class_cache_func() you might expect g_type_add_interface_check()

 - I move the setting of entry->init_state in gobject.c as you suggested,
   since it simplified the logic and cleaned things up.

   However, I did not add reentrancy guards as you suggested, because I 
   don't think reentrancy is possible. 

   type_iface_vtable_iface_init_Wm() is called in two circumstances:

   - When initializing interfaces after calling class_init()
   - When an interface is added to a class that has already been initialized

   But in no circumstances can either of these cases recurse - once an
   interface has been initialized for a particular class, it has been
   initialized and can't be initialized again until the class is unloaded.

   And unloading a class while the class or one of the interfaces of the
   class is being initialized just doesn't make sense.

Regards,
						Owen
Index: docs/reference/gobject/gobject-sections.txt
===================================================================
RCS file: /cvs/gnome/glib/docs/reference/gobject/gobject-sections.txt,v
retrieving revision 1.27
diff -u -p -r1.27 gobject-sections.txt
--- docs/reference/gobject/gobject-sections.txt	12 Sep 2003 20:37:29 -0000	1.27
+++ docs/reference/gobject/gobject-sections.txt	25 Sep 2003 23:34:40 -0000
@@ -88,6 +88,9 @@ g_type_free_instance
 g_type_add_class_cache_func
 g_type_remove_class_cache_func
 g_type_class_unref_uncached
+g_type_add_interface_check
+g_type_remove_interface_check
+GTypeInterfaceCheckFunc
 g_type_value_table_peek
 <SUBSECTION Private>
 G_TYPE_FUNDAMENTAL_SHIFT
Index: docs/reference/gobject/tmpl/gtype.sgml
===================================================================
RCS file: /cvs/gnome/glib/docs/reference/gobject/tmpl/gtype.sgml,v
retrieving revision 1.24
diff -u -p -r1.24 gtype.sgml
--- docs/reference/gobject/tmpl/gtype.sgml	2 Sep 2003 17:57:21 -0000	1.24
+++ docs/reference/gobject/tmpl/gtype.sgml	25 Sep 2003 23:34:40 -0000
@@ -1270,6 +1270,45 @@ fundamental types.
 @g_class: 
 
 
+<!-- ##### FUNCTION g_type_add_interface_check ##### -->
+<para>
+Adds a function to be called after an interface vtable is
+initialized for any class. That is, after the @interface_init
+member of #GInterfaceInfo has been called.
+</para>
+<para>
+This function is useful when you want to check an invariant
+that depends on the interfaces of a class. For instance,
+the implementation of #GObject uses this facility to check
+that an object implements all of the properties that are
+defined on its interfaces.    
+</para>
+
+ check_data: data to pass to @check_func
+ check_func: function to be called after each interface
+   is initialized.
+
+
+<!-- ##### FUNCTION g_type_remove_interface_check ##### -->
+<para>
+Removes an interface check function added with
+g_type_add_interface_check().
+</para>
+
+ check_data: callback data passed to g_type_add_interface_check()
+ chec_func: callback function passed to g_type_add_interface_check()
+
+
+<!-- ##### USER_FUNCTION GTypeInterfaceCheckFunc ##### -->
+<para>
+A callback called after an interface vtable is initialized.
+See g_type_add_interface_check().
+</para>
+
+ func_data: data passed to g_type_add_interface_check().
+ g_iface: the interface that has been initialized
+
+
 <!-- ##### FUNCTION g_type_value_table_peek ##### -->
 <para>
 Returns the location of the #GTypeValueTable associated with @type.
Index: gobject/gtype.c
===================================================================
RCS file: /cvs/gnome/glib/gobject/gtype.c,v
retrieving revision 1.66
diff -u -p -r1.66 gtype.c
--- gobject/gtype.c	2 Sep 2003 17:57:22 -0000	1.66
+++ gobject/gtype.c	25 Sep 2003 23:34:40 -0000
@@ -286,11 +286,17 @@ typedef struct {
   gpointer            cache_data;
   GTypeClassCacheFunc cache_func;
 } ClassCacheFunc;
+typedef struct {
+  gpointer                check_data;
+  GTypeInterfaceCheckFunc check_func;
+} IFaceCheckFunc;
 
 
 /* --- variables --- */
 static guint           static_n_class_cache_funcs = 0;
 static ClassCacheFunc *static_class_cache_funcs = NULL;
+static guint           static_n_iface_check_funcs = 0;
+static IFaceCheckFunc *static_iface_check_funcs = NULL;
 static GQuark          static_quark_type_flags = 0;
 static GQuark          static_quark_iface_holder = 0;
 static GQuark          static_quark_dependants_array = 0;
@@ -1642,6 +1648,8 @@ type_iface_vtable_base_init_Wm (TypeNode
 
   g_assert (iface->data && entry && entry->vtable == NULL && iholder && iholder->info);
   
+  entry->init_state = IFACE_INIT;
+
   pnode = lookup_type_node_I (NODE_PARENT_TYPE (node));
   if (pnode)	/* want to copy over parent iface contents */
     {
@@ -1673,16 +1681,15 @@ type_iface_vtable_iface_init_Wm (TypeNod
 				 TypeNode *node)
 {
   IFaceEntry *entry = type_lookup_iface_entry_L (node, iface);
-  IFaceHolder *iholder;
+  IFaceHolder *iholder = type_iface_peek_holder_L (iface, NODE_TYPE (node));
   GTypeInterface *vtable = NULL;
+  guint i;
   
-  iholder = type_iface_peek_holder_L (iface, NODE_TYPE (node));
-  if (!iholder)
-    return;
-
   /* iholder->info should have been filled in by type_iface_vtable_base_init_Wm() */
-  g_assert (iface->data && entry && iholder->info);
+  g_assert (iface->data && entry && iholder && iholder->info);
   
+  entry->init_state = INITIALIZED;
+      
   vtable = entry->vtable;
 
   if (iholder->info->interface_init)
@@ -1692,6 +1699,16 @@ type_iface_vtable_iface_init_Wm (TypeNod
 	iholder->info->interface_init (vtable, iholder->info->interface_data);
       G_WRITE_LOCK (&type_rw_lock);
     }
+  
+  for (i = 0; i < static_n_iface_check_funcs; i++)
+    {
+      GTypeInterfaceCheckFunc check_func = static_iface_check_funcs[i].check_func;
+      gpointer check_data = static_iface_check_funcs[i].check_data;
+
+      G_WRITE_UNLOCK (&type_rw_lock);
+      check_func (check_data, (gpointer)vtable);
+      G_WRITE_LOCK (&type_rw_lock);      
+    }
 }
 
 static gboolean
@@ -1804,8 +1821,6 @@ type_class_init_Wm (TypeNode   *node,
       if (i == CLASSED_NODE_N_IFACES (node))
 	break;
 
-      entry->init_state = IFACE_INIT;
-      
       if (!type_iface_vtable_base_init_Wm (lookup_type_node_I (entry->iface_type), node))
 	{
 	  guint j;
@@ -1865,8 +1880,6 @@ type_class_init_Wm (TypeNode   *node,
       if (i == CLASSED_NODE_N_IFACES (node))
 	break;
 
-      entry->init_state = INITIALIZED;
-      
       type_iface_vtable_iface_init_Wm (lookup_type_node_I (entry->iface_type), node);
       
       /* As in the loop above, additional initialized entries might be inserted
@@ -1891,29 +1904,12 @@ type_iface_vtable_init_Wm (TypeNode *nod
 {
   InitState class_state =
     node->data ? node->data->class.init_state : UNINITIALIZED;
-  InitState new_state = UNINITIALIZED;
   
   if (class_state >= BASE_IFACE_INIT)
-    {
-      type_iface_vtable_base_init_Wm (iface, node);
-      new_state = IFACE_INIT;
-    }
-
-  if (class_state >= IFACE_INIT)
-    {
-      type_iface_vtable_iface_init_Wm (iface, node);
-      new_state = INITIALIZED;
-    }
+    type_iface_vtable_base_init_Wm (iface, node);
   
-  if (class_state != UNINITIALIZED && class_state != INITIALIZED)
-    {
-      /* The interface was added while we were initializing the class
-       */
-      IFaceEntry *entry = type_lookup_iface_entry_L (node, iface);
-      g_assert (entry);
-      
-      entry->init_state = new_state;
-    }
+  if (class_state >= IFACE_INIT)
+    type_iface_vtable_iface_init_Wm (iface, node);
 }
 
 static void
@@ -2113,6 +2109,51 @@ g_type_remove_class_cache_func (gpointer
 	       cache_func, cache_data);
 }
 
+
+void
+g_type_add_interface_check (gpointer	            check_data,
+			    GTypeInterfaceCheckFunc check_func)
+{
+  guint i;
+  
+  g_return_if_fail (check_func != NULL);
+  
+  G_WRITE_LOCK (&type_rw_lock);
+  i = static_n_iface_check_funcs++;
+  static_iface_check_funcs = g_renew (IFaceCheckFunc, static_iface_check_funcs, static_n_iface_check_funcs);
+  static_iface_check_funcs[i].check_data = check_data;
+  static_iface_check_funcs[i].check_func = check_func;
+  G_WRITE_UNLOCK (&type_rw_lock);
+}
+
+void
+g_type_remove_interface_check (gpointer                check_data,
+			       GTypeInterfaceCheckFunc check_func)
+{
+  gboolean found_it = FALSE;
+  guint i;
+  
+  g_return_if_fail (check_func != NULL);
+  
+  G_WRITE_LOCK (&type_rw_lock);
+  for (i = 0; i < static_n_iface_check_funcs; i++)
+    if (static_iface_check_funcs[i].check_data == check_data &&
+	static_iface_check_funcs[i].check_func == check_func)
+      {
+	static_n_iface_check_funcs--;
+	g_memmove (static_iface_check_funcs + i,
+		   static_iface_check_funcs + i + 1,
+		   sizeof (static_iface_check_funcs[0]) * (static_n_iface_check_funcs - i));
+	static_iface_check_funcs = g_renew (IFaceCheckFunc, static_iface_check_funcs, static_n_iface_check_funcs);
+	found_it = TRUE;
+	break;
+      }
+  G_WRITE_UNLOCK (&type_rw_lock);
+  
+  if (!found_it)
+    g_warning (G_STRLOC ": cannot remove unregistered class check func %p with data %p",
+	       check_func, check_data);
+}
 
 /* --- type registration --- */
 GType
Index: gobject/gtype.h
===================================================================
RCS file: /cvs/gnome/glib/gobject/gtype.h,v
retrieving revision 1.49
diff -u -p -r1.49 gtype.h
--- gobject/gtype.h	2 Sep 2003 17:57:22 -0000	1.49
+++ gobject/gtype.h	25 Sep 2003 23:34:40 -0000
@@ -214,6 +214,8 @@ typedef void   (*GInterfaceFinalizeFunc)
 					      gpointer         iface_data);
 typedef gboolean (*GTypeClassCacheFunc)	     (gpointer	       cache_data,
 					      GTypeClass      *g_class);
+typedef void     (*GTypeInterfaceCheckFunc)  (gpointer	       func_data,
+					      gpointer         g_iface);
 typedef enum    /*< skip >*/
 {
   G_TYPE_FLAG_CLASSED           = (1 << 0),
@@ -313,11 +315,18 @@ GType		 g_type_fundamental_next	(void);
 GType		 g_type_fundamental		(GType		     type_id);
 GTypeInstance*   g_type_create_instance         (GType               type);
 void             g_type_free_instance           (GTypeInstance      *instance);
+
 void		 g_type_add_class_cache_func    (gpointer	     cache_data,
 						 GTypeClassCacheFunc cache_func);
 void		 g_type_remove_class_cache_func (gpointer	     cache_data,
 						 GTypeClassCacheFunc cache_func);
 void             g_type_class_unref_uncached    (gpointer            g_class);
+
+void             g_type_add_interface_check     (gpointer	         check_data,
+						 GTypeInterfaceCheckFunc check_func);
+void             g_type_remove_interface_check  (gpointer	         check_data,
+						 GTypeInterfaceCheckFunc chec_func);
+
 GTypeValueTable* g_type_value_table_peek        (GType		     type);
 
 
Index: tests/gobject/Makefile.am
===================================================================
RCS file: /cvs/gnome/glib/tests/gobject/Makefile.am,v
retrieving revision 1.1
diff -u -p -r1.1 Makefile.am
--- tests/gobject/Makefile.am	12 Sep 2003 20:37:09 -0000	1.1
+++ tests/gobject/Makefile.am	25 Sep 2003 23:34:40 -0000
@@ -44,6 +44,7 @@ LDADD = $(libgobject) libtestgobject.la
 
 test_programs =					\
 	accumulator				\
+	ifacecheck				\
 	ifaceinit				\
 	override
 
Index: tests/gobject/ifacecheck.c
===================================================================
RCS file: tests/gobject/ifacecheck.c
diff -N tests/gobject/ifacecheck.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/gobject/ifacecheck.c	25 Sep 2003 23:34:40 -0000
@@ -0,0 +1,169 @@
+/* GObject - GLib Type, Object, Parameter and Signal Library
+ * Copyright (C) 2001, 2003 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#undef	G_LOG_DOMAIN
+#define	G_LOG_DOMAIN "TestIfaceCheck"
+
+#undef G_DISABLE_ASSERT
+#undef G_DISABLE_CHECKS
+#undef G_DISABLE_CAST_CHECKS
+
+#include <string.h>
+
+#include <glib-object.h>
+
+#include "testcommon.h"
+
+/* This test tests g_type_add_interface_check_func(), which allows
+ * installing a post-initialization check function.
+ */
+
+#define TEST_TYPE_IFACE           (test_iface_get_type ())
+#define TEST_IFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TEST_TYPE_IFACE, TestIfaceClass))
+typedef struct _TestIfaceClass  TestIfaceClass;
+
+struct _TestIfaceClass
+{
+  GTypeInterface base_iface;
+  GString *history;
+};
+
+static void
+test_iface_base_init (TestIfaceClass *iface)
+{
+  if (iface->history)
+    g_string_free (iface->history, TRUE);
+  iface->history = g_string_new (NULL);
+}
+
+static DEFINE_IFACE(TestIface, test_iface, test_iface_base_init, NULL)
+
+/*
+ * TestObject1
+ */
+#define TEST_TYPE_OBJECT1         (test_object1_get_type ())
+typedef struct _GObject           TestObject1;
+typedef struct _GObjectClass      TestObject1Class;
+
+static DEFINE_TYPE_FULL (TestObject1, test_object1,
+			 NULL, NULL, NULL,
+			 G_TYPE_OBJECT,
+			 INTERFACE (NULL, TEST_TYPE_IFACE))
+     
+/*
+ * TestObject2
+ */
+#define TEST_TYPE_OBJECT2         (test_object2_get_type ())
+typedef struct _GObject           TestObject2;
+typedef struct _GObjectClass      TestObject2Class;
+
+static DEFINE_TYPE_FULL (TestObject2, test_object2,
+			 NULL, NULL, NULL,
+			 G_TYPE_OBJECT,
+			 INTERFACE (NULL, TEST_TYPE_IFACE))
+     
+/*
+ * TestObject3
+ */
+#define TEST_TYPE_OBJECT3         (test_object3_get_type ())
+typedef struct _GObject           TestObject3;
+typedef struct _GObjectClass      TestObject3Class;
+
+static DEFINE_TYPE_FULL (TestObject3, test_object3,
+			 NULL, NULL, NULL,
+			 G_TYPE_OBJECT,
+			 INTERFACE (NULL, TEST_TYPE_IFACE))
+     
+/*
+ * TestObject4
+ */
+#define TEST_TYPE_OBJECT4         (test_object4_get_type ())
+typedef struct _GObject           TestObject4;
+typedef struct _GObjectClass      TestObject4Class;
+
+
+static DEFINE_TYPE_FULL (TestObject4, test_object4,
+			 NULL, NULL, NULL,
+			 G_TYPE_OBJECT, {})
+
+static void
+check_func (gpointer check_data,
+	    gpointer g_iface)
+{
+  TestIfaceClass *iface = g_iface;
+
+  g_string_append (iface->history, check_data);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  TestIfaceClass *iface;
+  GObject *object;
+  char *string1 = "A";
+  char *string2 = "B";
+
+  g_type_init ();
+  
+  /* Basic check of interfaces added before class_init time
+   */
+  g_type_add_interface_check (string1, check_func);
+
+  object = g_object_new (TEST_TYPE_OBJECT1, NULL);
+  iface = TEST_IFACE_GET_CLASS (object);
+    g_assert (strcmp (iface->history->str, "A") == 0);
+  g_object_unref (object);
+
+  /* Add a second check function
+   */
+  g_type_add_interface_check (string2, check_func);
+
+  object = g_object_new (TEST_TYPE_OBJECT2, NULL);
+  iface = TEST_IFACE_GET_CLASS (object);
+  g_assert (strcmp (iface->history->str, "AB") == 0);
+  g_object_unref (object);
+
+  /* Remove the first check function
+   */
+  g_type_remove_interface_check (string1, check_func);
+
+  object = g_object_new (TEST_TYPE_OBJECT3, NULL);
+  iface = TEST_IFACE_GET_CLASS (object);
+  g_assert (strcmp (iface->history->str, "B") == 0);
+  g_object_unref (object);
+
+  /* Test interfaces added after class_init time
+   */
+  g_type_class_ref (TEST_TYPE_OBJECT4);
+  {
+    static GInterfaceInfo const iface = {
+      NULL, NULL, NULL
+    };
+    
+    g_type_add_interface_static (TEST_TYPE_OBJECT4, TEST_TYPE_IFACE, &iface);
+  }
+  
+  object = g_object_new (TEST_TYPE_OBJECT4, NULL);
+  iface = TEST_IFACE_GET_CLASS (object);
+  g_assert (strcmp (iface->history->str, "B") == 0);
+  g_object_unref (object);
+    
+  return 0;
+}


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