Add g_value_take_variant
authorChristian Persch <chpe@gnome.org>
Thu, 17 Jun 2010 23:08:13 +0000 (01:08 +0200)
committerChristian Persch <chpe@gnome.org>
Sat, 19 Jun 2010 17:07:36 +0000 (19:07 +0200)
Turns out we do need g_value_take_variant(), so we can correctly
implement VARIANT:* marshalers.

Bug #621947.

docs/reference/gobject/gobject-sections.txt
gobject/glib-genmarshal.c
gobject/gobject.symbols
gobject/gtype.h
gobject/gvaluetypes.c
gobject/gvaluetypes.h
tests/gobject/accumulator.c
tests/gobject/testmarshal.list

index 483c785..fbb4cfe 100644 (file)
@@ -708,6 +708,7 @@ g_param_spec_variant
 g_value_get_variant
 g_value_dup_variant
 g_value_set_variant
+g_value_take_variant
 
 <SUBSECTION Private>
 g_value_set_instance
index 30e36ad..e382fdc 100644 (file)
@@ -249,7 +249,7 @@ complete_out_arg (OutArgument *oarg)
     { "BOXED",         "BOXED",        "gpointer",     "g_value_take_boxed",                        },
     { "POINTER",       "POINTER",      "gpointer",     "g_value_set_pointer",                       },
     { "OBJECT",                "OBJECT",       "GObject*",     "g_value_take_object",                       },
-    { "VARIANT",       "VARIANT",      "GVariant*",    "g_value_set_variant",                       },
+    { "VARIANT",       "VARIANT",      "GVariant*",    "g_value_take_variant",                      },
     /* deprecated: */
     { "NONE",          "VOID",         "void",         NULL,                                        },
     { "BOOL",          "BOOLEAN",      "gboolean",     "g_value_set_boolean",                       },
index 9b09d9e..0e3c7ee 100644 (file)
@@ -299,6 +299,7 @@ g_value_get_gtype
 g_value_get_variant
 g_value_dup_variant
 g_value_set_variant
+g_value_take_variant
 #endif
 #endif
 
index d94249d..253d68e 100644 (file)
@@ -184,6 +184,13 @@ G_BEGIN_DECLS
  *
  * The fundamental type corresponding to #GVariant.
  *
+ * All floating #GVariant instances passed through the #GType system are
+ * consumed.
+ * 
+ * Note that callbacks in closures, and signal handlers
+ * for signals of return type %G_TYPE_VARIANT, must never return floating
+ * variants.
+ *
  * Note: GLib 2.24 did include a boxed type with this name. It was replaced
  * with this fundamental type in 2.26.
  *
index 6c54a27..1b15301 100644 (file)
@@ -1207,6 +1207,45 @@ g_value_set_variant (GValue   *value,
 }
 
 /**
+ * g_value_take_variant:
+ * @value: a valid #GValue of %G_TYPE_VARIANT
+ * @variant: a #GVariant, or %NULL
+ *
+ * Set the contents of a variant #GValue to @variant, and takes over
+ * the ownership of the caller's reference to @variant;
+ * the caller doesn't have to unref it any more (i.e. the reference
+ * count of the variant is not increased).
+ * 
+ * It is a programmer error to pass a floating variant to this function.
+ * In particular this means that callbacks in closures, and signal handlers
+ * for signals of return type %G_TYPE_VARIANT, must never return floating
+ * variants.
+ *
+ * If you want the #GValue to hold its own reference to @variant, use
+ * g_value_set_variant() instead.
+ *
+ * This is an internal function introduced mainly for C marshallers.
+ *
+ * Since: 2.26
+ */
+void
+g_value_take_variant (GValue   *value,
+                      GVariant *variant)
+{
+  GVariant *old_variant;
+
+  g_return_if_fail (G_VALUE_HOLDS_VARIANT (value));
+  g_return_if_fail (variant == NULL || !g_variant_is_floating (variant));
+
+  old_variant = value->data[0].v_pointer;
+
+  value->data[0].v_pointer = variant;
+
+  if (old_variant)
+    g_variant_unref (old_variant);
+}
+
+/**
  * g_value_get_variant:
  * @value: a valid #GValue of %G_TYPE_VARIANT
  *
index d5e27c9..3d3f7b6 100644 (file)
@@ -225,6 +225,8 @@ void                      g_value_set_gtype         (GValue       *value,
 GType                g_value_get_gtype         (const GValue *value);
 void                 g_value_set_variant       (GValue       *value,
                                                 GVariant     *variant);
+void                 g_value_take_variant      (GValue       *value,
+                                                GVariant     *variant);
 GVariant*            g_value_get_variant       (const GValue *value);
 GVariant*            g_value_dup_variant       (const GValue *value);
 
index 602ab11..a4aaee9 100644 (file)
@@ -58,6 +58,8 @@ struct _TestObjectClass
                            gint        param);
   gboolean (*test_signal2) (TestObject *tobject,
                            gint        param);
+  GVariant* (*test_signal3) (TestObject *tobject,
+                             gboolean *weak_ptr);
 };
 
 static GType test_object_get_type (void);
@@ -155,11 +157,70 @@ test_object_signal2_callback_after (TestObject *tobject,
   return FALSE;
 }
 
+static gboolean
+test_signal3_accumulator (GSignalInvocationHint *ihint,
+                         GValue                *return_accu,
+                         const GValue          *handler_return,
+                         gpointer               data)
+{
+  GVariant *variant;
+
+  variant = g_value_get_variant (handler_return);
+  g_assert (!g_variant_is_floating (variant));
+
+  g_value_set_variant (return_accu, variant);
+
+  return variant == NULL;
+}
+
+/* To be notified when the variant is finalised, we construct
+ * it from data with a custom GDestroyNotify.
+ */
+
+typedef struct {
+  char *mem;
+  gsize n;
+  gboolean *weak_ptr;
+} VariantData;
+
+static void
+free_data (VariantData *data)
+{
+  *(data->weak_ptr) = TRUE;
+  g_free (data->mem);
+  g_slice_free (VariantData, data);
+}
+
+static GVariant *
+test_object_real_signal3 (TestObject *tobject,
+                          gboolean *weak_ptr)
+{
+  GVariant *variant;
+  VariantData *data;
+
+  variant = g_variant_ref_sink (g_variant_new_uint32 (42));
+  data = g_slice_new (VariantData);
+  data->weak_ptr = weak_ptr;
+  data->n = g_variant_get_size (variant);
+  data->mem = g_malloc (data->n);
+  g_variant_store (variant, data->mem);
+  g_variant_unref (variant);
+
+  variant = g_variant_new_from_data (G_VARIANT_TYPE ("u"),
+                                     data->mem,
+                                     data->n,
+                                     TRUE,
+                                     (GDestroyNotify) free_data,
+                                     data);
+  return g_variant_ref_sink (variant);
+}
+
 static void
 test_object_class_init (TestObjectClass *class)
 {
   class->test_signal1 = test_object_real_signal1;
   class->test_signal2 = test_object_real_signal2;
+  class->test_signal3 = test_object_real_signal3;
   
   g_signal_new ("test-signal1",
                G_OBJECT_CLASS_TYPE (class),
@@ -175,6 +236,13 @@ test_object_class_init (TestObjectClass *class)
                g_signal_accumulator_true_handled, NULL,
                test_marshal_BOOLEAN__INT,
                G_TYPE_BOOLEAN, 1, G_TYPE_INT);
+  g_signal_new ("test-signal3",
+               G_OBJECT_CLASS_TYPE (class),
+               G_SIGNAL_RUN_LAST,
+               G_STRUCT_OFFSET (TestObjectClass, test_signal3),
+               test_signal3_accumulator, NULL,
+               test_marshal_VARIANT__POINTER,
+               G_TYPE_VARIANT, 1, G_TYPE_POINTER);
 }
 
 static DEFINE_TYPE(TestObject, test_object,
@@ -188,6 +256,8 @@ main (int   argc,
   TestObject *object;
   gchar *string_result;
   gboolean bool_result;
+  gboolean variant_finalised;
+  GVariant *variant_result;
        
   g_log_set_always_fatal (g_log_set_always_fatal (G_LOG_FATAL_MASK) |
                          G_LOG_LEVEL_WARNING |
@@ -223,5 +293,16 @@ main (int   argc,
   g_signal_emit_by_name (object, "test-signal2", 4, &bool_result);
   g_assert (bool_result == FALSE);
 
+  variant_finalised = FALSE;
+  variant_result = NULL;
+  g_signal_emit_by_name (object, "test-signal3", &variant_finalised, &variant_result);
+  g_assert (variant_result != NULL);
+  g_assert (!g_variant_is_floating (variant_result));
+
+  /* Test that variant_result had refcount 1 */
+  g_assert (!variant_finalised);
+  g_variant_unref (variant_result);
+  g_assert (variant_finalised);
+
   return 0;
 }
index bed97e2..198c4f9 100644 (file)
@@ -1,4 +1,4 @@
 # Marshallers used in tests
 BOOLEAN:INT
 STRING:INT
-
+VARIANT:POINTER