[glib/wip/alexl/versioned-types] Support versions of GTypes



commit a10d9e54642f0b09fac8f65bf1fe7ad3721e02f2
Author: Alexander Larsson <alexl redhat com>
Date:   Fri Jan 29 18:27:14 2016 +0100

    Support versions of GTypes
    
    This adds support for registrating multiple versions of the same
    typename by adding "@@" and a version name when you register the type.
    
    You can query the versions of a type, and you can look up a name by
    name + version with g_type_from_name_and_version(). g_type_from_name()
    currently picks essentially a random (last registred) type in the case
    of a name that has multiple versions. More work is needed to decide what
    the best approach is for that.
    
    This allows you to include a static library in your module with a
    custom version, which will not conflict with other copies of the same code.
    It also allows multiple shared libraries with the same type if you use
    versioned symbols.

 gobject/gboxed.c      |    2 +-
 gobject/gtype.c       |  205 ++++++++++++++++++++++++++++++++++++++++++++-----
 gobject/gtype.h       |   23 +++++-
 gobject/gvaluetypes.c |    2 +-
 4 files changed, 206 insertions(+), 26 deletions(-)
---
diff --git a/gobject/gboxed.c b/gobject/gboxed.c
index 40723f5..146ee29 100644
--- a/gobject/gboxed.c
+++ b/gobject/gboxed.c
@@ -308,7 +308,7 @@ g_boxed_type_register_static (const gchar   *name,
   g_return_val_if_fail (name != NULL, 0);
   g_return_val_if_fail (boxed_copy != NULL, 0);
   g_return_val_if_fail (boxed_free != NULL, 0);
-  g_return_val_if_fail (g_type_from_name (name) == 0, 0);
+  //g_return_val_if_fail (g_type_from_name (name) == 0, 0);
 
   type = g_type_register_static (G_TYPE_BOXED, name, &type_info, 0);
 
diff --git a/gobject/gtype.c b/gobject/gtype.c
index eb416ab..5d2ea6d 100644
--- a/gobject/gtype.c
+++ b/gobject/gtype.c
@@ -244,6 +244,8 @@ struct _TypeNode
   GType       *children; /* writable with lock */
   TypeData * volatile data;
   GQuark       qname;
+  GQuark       qversion;
+  TypeNode    *next_with_same_name;
   GData       *global_gdata;
   union {
     GAtomicArray iface_entries;                /* for !iface types */
@@ -262,6 +264,7 @@ struct _TypeNode
 #define NODE_PARENT_TYPE(node)                 (node->supers[1])
 #define NODE_FUNDAMENTAL_TYPE(node)            (node->supers[node->n_supers])
 #define NODE_NAME(node)                                (g_quark_to_string (node->qname))
+#define NODE_VERSION(node)                     (g_quark_to_string (node->qversion))
 #define NODE_REFCOUNT(node)                     ((guint) g_atomic_int_get ((int *) &(node)->ref_count))
 #define        NODE_IS_BOXED(node)                     (NODE_FUNDAMENTAL_TYPE (node) == G_TYPE_BOXED)
 #define        NODE_IS_IFACE(node)                     (NODE_FUNDAMENTAL_TYPE (node) == G_TYPE_INTERFACE)
@@ -428,11 +431,12 @@ static TypeNode*
 type_node_any_new_W (TypeNode             *pnode,
                     GType                 ftype,
                     const gchar          *name,
+                    const gchar          *version,
                     GTypePlugin          *plugin,
                     GTypeFundamentalFlags type_flags)
 {
   guint n_supers;
-  GType type;
+  GType type, old_type;
   TypeNode *node;
   guint i, node_size = 0;
 
@@ -516,7 +520,13 @@ type_node_any_new_W (TypeNode             *pnode,
   node->children = NULL;
   node->data = NULL;
   node->qname = g_quark_from_string (name);
+  if (version)
+    node->qversion = g_quark_from_string (version);
   node->global_gdata = NULL;
+
+  old_type = (GType)g_hash_table_lookup (static_type_nodes_ht, name);
+  if (old_type)
+    node->next_with_same_name = lookup_type_node_I (old_type);
   g_hash_table_insert (static_type_nodes_ht,
                       (gpointer) g_quark_to_string (node->qname),
                       (gpointer) type);
@@ -553,7 +563,7 @@ type_node_fundamental_new_W (GType                 ftype,
   
   type_flags &= TYPE_FUNDAMENTAL_FLAG_MASK;
   
-  node = type_node_any_new_W (NULL, ftype, name, NULL, type_flags);
+  node = type_node_any_new_W (NULL, ftype, name, NULL, NULL, type_flags);
   
   finfo = type_node_fundamental_info_I (node);
   finfo->type_flags = type_flags;
@@ -564,14 +574,16 @@ type_node_fundamental_new_W (GType                 ftype,
 static TypeNode*
 type_node_new_W (TypeNode    *pnode,
                 const gchar *name,
+                const gchar *version,
                 GTypePlugin *plugin)
      
 {
   g_assert (pnode);
   g_assert (pnode->n_supers < MAX_N_SUPERS);
   g_assert (pnode->n_children < MAX_N_CHILDREN);
-  
-  return type_node_any_new_W (pnode, NODE_FUNDAMENTAL_TYPE (pnode), name, plugin, 0);
+
+  return type_node_any_new_W (pnode, NODE_FUNDAMENTAL_TYPE (pnode),
+                              name, version, plugin, 0);
 }
 
 static inline IFaceEntry*
@@ -740,8 +752,33 @@ check_plugin_U (GTypePlugin *plugin,
   return TRUE;
 }
 
+static void split_type_and_version (const gchar *type_name_and_version,
+                                    gchar **name_out,
+                                    const gchar **version_out)
+{
+  const char *version;
+
+  version = strstr (type_name_and_version, "@@");
+  if (version)
+    {
+      *name_out = g_strndup (type_name_and_version, version - type_name_and_version);
+      version += 2;
+
+      /* Empty version is same as no version */
+      if (*version == 0)
+        *version_out = NULL;
+      else
+        *version_out = version;
+    }
+  else
+    {
+      *name_out = g_strdup (type_name_and_version);
+      *version_out = NULL;
+    }
+}
+
 static gboolean
-check_type_name_I (const gchar *type_name)
+check_type_name_I (const gchar *type_name, const char *version)
 {
   static const gchar extra_chars[] = "-_+";
   const gchar *p = type_name;
@@ -764,9 +801,12 @@ check_type_name_I (const gchar *type_name)
       g_warning ("type name '%s' contains invalid characters", type_name);
       return FALSE;
     }
-  if (g_type_from_name (type_name))
+  if (g_type_from_name_and_version (type_name, version))
     {
-      g_warning ("cannot register existing type '%s'", type_name);
+      if (version)
+        g_warning ("cannot register existing type '%s' version '%s'", type_name, version);
+      else
+        g_warning ("cannot register existing type '%s'", type_name);
       return FALSE;
     }
   
@@ -2638,7 +2678,7 @@ g_type_register_fundamental (GType                       type_id,
   g_return_val_if_fail (info != NULL, 0);
   g_return_val_if_fail (finfo != NULL, 0);
   
-  if (!check_type_name_I (type_name))
+  if (!check_type_name_I (type_name, NULL))
     return 0;
   if ((type_id & TYPE_ID_MASK) ||
       type_id > G_TYPE_FUNDAMENTAL_MAX)
@@ -2742,23 +2782,32 @@ g_type_register_static_simple (GType             parent_type,
  */
 GType
 g_type_register_static (GType            parent_type,
-                       const gchar     *type_name,
+                       const gchar     *type_name_and_version,
                        const GTypeInfo *info,
                        GTypeFlags       flags)
 {
   TypeNode *pnode, *node;
   GType type = 0;
+  gchar *type_name;
+  const gchar *version;
   
   g_assert_type_system_initialized ();
   g_return_val_if_fail (parent_type > 0, 0);
-  g_return_val_if_fail (type_name != NULL, 0);
+  g_return_val_if_fail (type_name_and_version != NULL, 0);
   g_return_val_if_fail (info != NULL, 0);
+
+  split_type_and_version (type_name_and_version, &type_name, &version);
   
-  if (!check_type_name_I (type_name) ||
+  if (!check_type_name_I (type_name, version) ||
       !check_derivation_I (parent_type, type_name))
-    return 0;
+    {
+      g_free (type_name);
+      return 0;
+    }
+
   if (info->class_finalize)
     {
+      g_free (type_name);
       g_warning ("class finalizer specified for static type '%s'",
                 type_name);
       return 0;
@@ -2769,13 +2818,15 @@ g_type_register_static (GType            parent_type,
   type_data_ref_Wm (pnode);
   if (check_type_info_I (pnode, NODE_FUNDAMENTAL_TYPE (pnode), type_name, info))
     {
-      node = type_node_new_W (pnode, type_name, NULL);
+      node = type_node_new_W (pnode, type_name, version, NULL);
       type_add_flags_W (node, flags);
       type = NODE_TYPE (node);
       type_data_make_W (node, info,
                        check_value_table_I (type_name, info->value_table) ? info->value_table : NULL);
     }
   G_WRITE_UNLOCK (&type_rw_lock);
+
+  g_free (type_name);
   
   return type;
 }
@@ -2783,7 +2834,7 @@ g_type_register_static (GType            parent_type,
 /**
  * g_type_register_dynamic:
  * @parent_type: type from which this type will be derived
- * @type_name: 0-terminated string used as the name of the new type
+ * @type_name_and_version: 0-terminated string used as the name of the new type
  * @plugin: #GTypePlugin structure to retrieve the #GTypeInfo from
  * @flags: bitwise combination of #GTypeFlags values
  *
@@ -2797,29 +2848,38 @@ g_type_register_static (GType            parent_type,
  */
 GType
 g_type_register_dynamic (GType        parent_type,
-                        const gchar *type_name,
+                        const gchar *type_name_and_version,
                         GTypePlugin *plugin,
                         GTypeFlags   flags)
 {
   TypeNode *pnode, *node;
   GType type;
+  gchar *type_name;
+  const gchar *version;
   
   g_assert_type_system_initialized ();
   g_return_val_if_fail (parent_type > 0, 0);
-  g_return_val_if_fail (type_name != NULL, 0);
+  g_return_val_if_fail (type_name_and_version != NULL, 0);
   g_return_val_if_fail (plugin != NULL, 0);
+
+  split_type_and_version (type_name_and_version, &type_name, &version);
   
-  if (!check_type_name_I (type_name) ||
+  if (!check_type_name_I (type_name, version) ||
       !check_derivation_I (parent_type, type_name) ||
       !check_plugin_U (plugin, TRUE, FALSE, type_name))
-    return 0;
+    {
+      g_free (type_name);
+      return 0;
+    }
   
   G_WRITE_LOCK (&type_rw_lock);
   pnode = lookup_type_node_I (parent_type);
-  node = type_node_new_W (pnode, type_name, plugin);
+  node = type_node_new_W (pnode, type_name, version, plugin);
   type_add_flags_W (node, flags);
   type = NODE_TYPE (node);
   G_WRITE_UNLOCK (&type_rw_lock);
+
+  g_free (type_name);
   
   return type;
 }
@@ -3345,6 +3405,52 @@ g_type_qname (GType type)
 }
 
 /**
+ * g_type_qversion:
+ * @type: type to return quark of type version for
+ *
+ * Get the corresponding quark of the type IDs version.
+ *
+ * Returns: the type version quark or 0
+ *
+ * Since: 2.48
+ */
+GQuark
+g_type_qversion (GType type)
+{
+  TypeNode *node;
+
+  node = lookup_type_node_I (type);
+
+  return node ? node->qversion : 0;
+}
+
+/**
+ * g_type_version:
+ * @type: type to return version for
+ *
+ * Get the unique versoin that is assigned to a type ID.  Note that this
+ * function (like all other GType API) cannot cope with invalid type
+ * IDs. %G_TYPE_INVALID may be passed to this function, as may be any
+ * other validly registered type ID, but randomized type IDs should
+ * not be passed in and will most likely lead to a crash.
+ *
+ * Returns: static type version or %NULL
+ *
+ * Since: 2.48
+ */
+const gchar *
+g_type_version (GType type)
+{
+  TypeNode *node;
+
+  g_assert_type_system_initialized ();
+
+  node = lookup_type_node_I (type);
+
+  return node ? NODE_VERSION (node) : NULL;
+}
+
+/**
  * g_type_from_name:
  * @name: type name to lookup
  *
@@ -3359,11 +3465,72 @@ GType
 g_type_from_name (const gchar *name)
 {
   GType type = 0;
+  TypeNode *node;
+
+  g_return_val_if_fail (name != NULL, 0);
+
+  G_READ_LOCK (&type_rw_lock);
+  type = (GType) g_hash_table_lookup (static_type_nodes_ht, name);
+
+  if (type != 0)
+    {
+      node = lookup_type_node_I (type);
+      if (node->next_with_same_name != NULL)
+        g_warning ("Looking up by typename %s which has multiple registred versions", name);
+    }
+
+  G_READ_UNLOCK (&type_rw_lock);
+
+  return type;
+}
+
+/**
+ * g_type_from_name_and_version:
+ * @name: type name to lookup
+ * @version: (nullable): type version to lookup, or %NULL for default
+ *
+ * Lookup the type ID from a given type name and version, returning 0 if no type
+ * has been registered under this name and version (this is the preferred method
+ * to find out by name whether a specific type has been registered
+ * yet).
+ *
+ * Returns: corresponding type ID or 0
+ */
+GType
+g_type_from_name_and_version (const gchar     *name,
+                              const gchar     *version)
+{
+  GType type = 0;
+  TypeNode *node;
   
   g_return_val_if_fail (name != NULL, 0);
   
   G_READ_LOCK (&type_rw_lock);
   type = (GType) g_hash_table_lookup (static_type_nodes_ht, name);
+
+  if (type != 0)
+    {
+      GQuark qversion = 0;
+      node = lookup_type_node_I (type);
+
+      type = 0;
+      if (version)
+        qversion = g_quark_try_string (version);
+      if (qversion != 0 || version == NULL)
+        {
+          while (node != NULL)
+            {
+              if (node->qversion == qversion)
+                {
+                  type = NODE_TYPE (node);
+                  goto out;
+                }
+              node = node->next_with_same_name;
+            }
+        }
+    }
+
+ out:
   G_READ_UNLOCK (&type_rw_lock);
   
   return type;
diff --git a/gobject/gtype.h b/gobject/gtype.h
index b564bbb..8f7ae6a 100644
--- a/gobject/gtype.h
+++ b/gobject/gtype.h
@@ -685,8 +685,15 @@ GLIB_AVAILABLE_IN_ALL
 const gchar *         g_type_name                    (GType            type);
 GLIB_AVAILABLE_IN_ALL
 GQuark                g_type_qname                   (GType            type);
+GLIB_AVAILABLE_IN_2_48
+const gchar *         g_type_version                 (GType            type);
+GLIB_AVAILABLE_IN_2_48
+GQuark                g_type_qversion                (GType            type);
 GLIB_AVAILABLE_IN_ALL
 GType                 g_type_from_name               (const gchar     *name);
+GLIB_AVAILABLE_IN_2_48
+GType                 g_type_from_name_and_version   (const gchar     *name,
+                                                      const gchar     *version);
 GLIB_AVAILABLE_IN_ALL
 GType                 g_type_parent                  (GType            type);
 GLIB_AVAILABLE_IN_ALL
@@ -1938,6 +1945,12 @@ static void     type_name##_class_intern_init (gpointer klass) \
 }
 #endif /* GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38 */
 
+#ifdef G_DEFINE_TYPE_DEFAULT_VERSION
+#define _G_DEFINE_TYPE_GET_VERSIONED_TYPENAME(TypeName) #TypeName "@@" 
G_STRINGIFY(G_DEFINE_TYPE_DEFAULT_VERSION)
+#else
+#define _G_DEFINE_TYPE_GET_VERSIONED_TYPENAME(TypeName) #TypeName
+#endif
+
 #define _G_DEFINE_TYPE_EXTENDED_BEGIN(TypeName, type_name, TYPE_PARENT, flags) \
 \
 static void     type_name##_init              (TypeName        *self); \
@@ -1962,7 +1975,7 @@ type_name##_get_type (void) \
     { \
       GType g_define_type_id = \
         g_type_register_static_simple (TYPE_PARENT, \
-                                       g_intern_static_string (#TypeName), \
+                                       g_intern_static_string 
(_G_DEFINE_TYPE_GET_VERSIONED_TYPENAME(TypeName)), \
                                        sizeof (TypeName##Class), \
                                        (GClassInitFunc) type_name##_class_intern_init, \
                                        sizeof (TypeName), \
@@ -1989,7 +2002,7 @@ type_name##_get_type (void) \
     { \
       GType g_define_type_id = \
         g_type_register_static_simple (G_TYPE_INTERFACE, \
-                                       g_intern_static_string (#TypeName), \
+                                       g_intern_static_string 
(_G_DEFINE_TYPE_GET_VERSIONED_TYPENAME(TypeName)), \
                                        sizeof (TypeName##Interface), \
                                        (GClassInitFunc)type_name##_default_init, \
                                        0, \
@@ -2074,7 +2087,7 @@ type_name##_get_type (void) \
            } __attribute__((__transparent_union__)) \
         ) = g_boxed_type_register_static; \
       GType g_define_type_id = \
-        _g_register_boxed (g_intern_static_string (#TypeName), copy_func, free_func); \
+        _g_register_boxed (g_intern_static_string (_G_DEFINE_TYPE_GET_VERSIONED_TYPENAME(TypeName)), 
copy_func, free_func); \
       { /* custom code follows */
 #else
 #define _G_DEFINE_BOXED_TYPE_BEGIN(TypeName, type_name, copy_func, free_func) \
@@ -2085,7 +2098,7 @@ type_name##_get_type (void) \
   if (g_once_init_enter (&g_define_type_id__volatile))  \
     { \
       GType g_define_type_id = \
-        g_boxed_type_register_static (g_intern_static_string (#TypeName), \
+        g_boxed_type_register_static (g_intern_static_string 
(_G_DEFINE_TYPE_GET_VERSIONED_TYPENAME(TypeName)), \
                                       (GBoxedCopyFunc) copy_func, \
                                       (GBoxedFreeFunc) free_func); \
       { /* custom code follows */
@@ -2126,7 +2139,7 @@ type_name##_get_type (void) \
   if (g_once_init_enter (&g_define_type_id__volatile))  \
     { \
       GType g_define_type_id = \
-        g_pointer_type_register_static (g_intern_static_string (#TypeName)); \
+        g_pointer_type_register_static (g_intern_static_string 
(_G_DEFINE_TYPE_GET_VERSIONED_TYPENAME(TypeName))); \
       { /* custom code follows */
 
 /* --- protected (for fundamental type implementations) --- */
diff --git a/gobject/gvaluetypes.c b/gobject/gvaluetypes.c
index 4b234a3..aa4e103 100644
--- a/gobject/gvaluetypes.c
+++ b/gobject/gvaluetypes.c
@@ -1437,7 +1437,7 @@ g_pointer_type_register_static (const gchar *name)
   GType type;
 
   g_return_val_if_fail (name != NULL, 0);
-  g_return_val_if_fail (g_type_from_name (name) == 0, 0);
+  //g_return_val_if_fail (g_type_from_name (name) == 0, 0);
 
   type = g_type_register_static (G_TYPE_POINTER, name, &type_info, 0);
 


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