structure: add gst_structure_*_get*() vararg functions
authorTim-Philipp Müller <tim.muller@collabora.co.uk>
Mon, 8 Jun 2009 22:43:16 +0000 (23:43 +0100)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Wed, 10 Jun 2009 08:39:12 +0000 (09:39 +0100)
Add a bunch of vararg getter convenience functions to complement
the vararg setter functions, and a basic unit test. Fixes #534208.

API: gst_structure_get()
API: gst_structure_id_get()
API: gst_structure_get_valist()
API: gst_structure_id_get_valist()

docs/gst/gstreamer-sections.txt
gst/gststructure.c
gst/gststructure.h
tests/check/gst/gststructure.c
win32/common/libgstreamer.def

index 048d24f..df5fb4c 100644 (file)
@@ -2012,8 +2012,12 @@ gst_structure_get_name
 gst_structure_has_name
 gst_structure_set_name
 gst_structure_get_name_id
+gst_structure_id_get
+gst_structure_id_get_valist
 gst_structure_id_get_value
 gst_structure_id_set_value
+gst_structure_get
+gst_structure_get_valist
 gst_structure_get_value
 gst_structure_set_value
 gst_structure_set
index 8962321..e446b7f 100644 (file)
@@ -2342,3 +2342,250 @@ gst_structure_fixate_field_nearest_fraction (GstStructure * structure,
 
   return FALSE;
 }
+
+/* our very own version of G_VALUE_LCOPY that allows NULL return locations
+ * (useful for message parsing functions where the return location is user
+ * supplied and the user may pass NULL if the value isn't of interest) */
+#define GST_VALUE_LCOPY(value, var_args, flags, __error, fieldname)           \
+G_STMT_START {                                                                \
+  const GValue *_value = (value);                                             \
+  guint _flags = (flags);                                                     \
+  GType _value_type = G_VALUE_TYPE (_value);                                  \
+  GTypeValueTable *_vtable = g_type_value_table_peek (_value_type);           \
+  gchar *_lcopy_format = _vtable->lcopy_format;                               \
+  GTypeCValue _cvalues[G_VALUE_COLLECT_FORMAT_MAX_LENGTH] = { { 0, }, };      \
+  guint _n_values = 0;                                                        \
+                                                                              \
+  while (*_lcopy_format != '\0') {                                            \
+    g_assert (*_lcopy_format == G_VALUE_COLLECT_POINTER);                     \
+    _cvalues[_n_values++].v_pointer = va_arg ((var_args), gpointer);          \
+    _lcopy_format++;                                                          \
+  }                                                                           \
+  if (_n_values == 2 && !!_cvalues[0].v_pointer != !!_cvalues[1].v_pointer) { \
+    *(__error) = g_strdup_printf ("either all or none of the return "         \
+        "locations for field '%s' need to be NULL", fieldname);               \
+  } else if (_cvalues[0].v_pointer != NULL) {                                 \
+    *(__error) = _vtable->lcopy_value (_value, _n_values, _cvalues, _flags);  \
+  }                                                                           \
+} G_STMT_END
+
+/**
+ * gst_structure_get_valist:
+ * @structure: a #GstStructure
+ * @first_fieldname: the name of the first field to read
+ * @valist: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * valist-variant of gst_structure_get(). Look at the documentation of
+ * gst_structure_get() for more details.
+ *
+ * Returns: TRUE, or FALSE if there was a problem reading any of the fields
+ *
+ * Since: 0.10.24
+ */
+gboolean
+gst_structure_get_valist (GstStructure * structure,
+    const char *first_fieldname, va_list args)
+{
+  const char *field_name;
+  GType expected_type = G_TYPE_INVALID;
+
+  g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+  g_return_val_if_fail (first_fieldname != NULL, FALSE);
+
+  field_name = first_fieldname;
+  while (field_name) {
+    const GValue *val = NULL;
+    gchar *err = NULL;
+
+    expected_type = va_arg (args, GType);
+
+    val = gst_structure_get_value (structure, field_name);
+
+    if (val == NULL)
+      goto no_such_field;
+
+    if (G_VALUE_TYPE (val) != expected_type)
+      goto wrong_type;
+
+    GST_VALUE_LCOPY (val, args, 0, &err, field_name);
+    if (err) {
+      g_warning ("%s: %s", G_STRFUNC, err);
+      g_free (err);
+      return FALSE;
+    }
+
+    field_name = va_arg (args, const gchar *);
+  }
+
+  return TRUE;
+
+/* ERRORS */
+no_such_field:
+  {
+    GST_WARNING ("Expected field '%s' in structure: %" GST_PTR_FORMAT,
+        field_name, structure);
+    return FALSE;
+  }
+wrong_type:
+  {
+    GST_WARNING ("Expected field '%s' in structure to be of type '%s', but "
+        "field was of type '%s': %" GST_PTR_FORMAT, field_name,
+        GST_STR_NULL (g_type_name (expected_type)),
+        G_VALUE_TYPE_NAME (gst_structure_get_value (structure, field_name)),
+        structure);
+    return FALSE;
+  }
+}
+
+/**
+ * gst_structure_id_get_valist:
+ * @structure: a #GstStructure
+ * @first_field_id: the quark of the first field to read
+ * @valist: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * valist-variant of gst_structure_id_get(). Look at the documentation of
+ * gst_structure_id_get() for more details.
+ *
+ * Returns: TRUE, or FALSE if there was a problem reading any of the fields
+ *
+ * Since: 0.10.24
+ */
+gboolean
+gst_structure_id_get_valist (GstStructure * structure, GQuark first_field_id,
+    va_list args)
+{
+  GQuark field_id;
+  GType expected_type = G_TYPE_INVALID;
+
+  g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+  g_return_val_if_fail (first_field_id != 0, FALSE);
+
+  field_id = first_field_id;
+  while (field_id) {
+    const GValue *val = NULL;
+    gchar *err = NULL;
+
+    expected_type = va_arg (args, GType);
+
+    val = gst_structure_id_get_value (structure, field_id);
+
+    if (val == NULL)
+      goto no_such_field;
+
+    if (G_VALUE_TYPE (val) != expected_type)
+      goto wrong_type;
+
+    GST_VALUE_LCOPY (val, args, 0, &err, g_quark_to_string (field_id));
+    if (err) {
+      g_warning ("%s: %s", G_STRFUNC, err);
+      g_free (err);
+      return FALSE;
+    }
+
+    field_id = va_arg (args, GQuark);
+  }
+
+  return TRUE;
+
+/* ERRORS */
+no_such_field:
+  {
+    GST_WARNING ("Expected field '%s' in structure: %" GST_PTR_FORMAT,
+        GST_STR_NULL (g_quark_to_string (field_id)), structure);
+    return FALSE;
+  }
+wrong_type:
+  {
+    GST_WARNING ("Expected field '%s' in structure to be of type '%s', but "
+        "field was of type '%s': %" GST_PTR_FORMAT,
+        g_quark_to_string (field_id),
+        GST_STR_NULL (g_type_name (expected_type)),
+        G_VALUE_TYPE_NAME (gst_structure_id_get_value (structure, field_id)),
+        structure);
+    return FALSE;
+  }
+}
+
+/**
+ * gst_structure_get:
+ * @structure: a #GstStructure
+ * @first_fieldname: the name of the first field to read
+ * @...: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * Variable arguments should be in the form field name, field type
+ * (as a GType), pointer(s) to a variable(s) to hold the return value(s).
+ * The last variable argument should be NULL.
+ *
+ * For refcounted (mini)objects you will acquire your own reference which
+ * you must release with a suitable _unref() when no longer needed. For
+ * strings and boxed types you will acquire a copy which you will need to
+ * release with either g_free() or the suiteable function for the boxed type.
+ *
+ * Returns: FALSE if there was a problem reading any of the fields (e.g.
+ *     because the field requested did not exist, or was of a type other
+ *     than the type specified), otherwise TRUE.
+ *
+ * Since: 0.10.24
+ */
+gboolean
+gst_structure_get (GstStructure * structure, const char *first_fieldname, ...)
+{
+  gboolean ret;
+  va_list args;
+
+  g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+  g_return_val_if_fail (first_fieldname != NULL, FALSE);
+
+  va_start (args, first_fieldname);
+  ret = gst_structure_get_valist (structure, first_fieldname, args);
+  va_end (args);
+
+  return ret;
+}
+
+/**
+ * gst_structure_id_get:
+ * @structure: a #GstStructure
+ * @first_field_id: the quark of the first field to read
+ * @...: variable arguments
+ *
+ * Parses the variable arguments and reads fields from @structure accordingly.
+ * Variable arguments should be in the form field id quark, field type
+ * (as a GType), pointer(s) to a variable(s) to hold the return value(s).
+ * The last variable argument should be NULL (technically it should be a
+ * 0 quark, but we require NULL so compilers that support it can check for
+ * the NULL terminator and warn if it's not there).
+ *
+ * This function is just like gst_structure_get() only that it is slightly
+ * more efficient since it saves the string-to-quark lookup in the global
+ * quark hashtable.
+ *
+ * For refcounted (mini)objects you will acquire your own reference which
+ * you must release with a suitable _unref() when no longer needed. For
+ * strings and boxed types you will acquire a copy which you will need to
+ * release with either g_free() or the suiteable function for the boxed type.
+ *
+ * Returns: FALSE if there was a problem reading any of the fields (e.g.
+ *     because the field requested did not exist, or was of a type other
+ *     than the type specified), otherwise TRUE.
+ *
+ * Since: 0.10.24
+ */
+gboolean
+gst_structure_id_get (GstStructure * structure, GQuark first_field_id, ...)
+{
+  gboolean ret;
+  va_list args;
+
+  g_return_val_if_fail (GST_IS_STRUCTURE (structure), FALSE);
+  g_return_val_if_fail (first_field_id != 0, FALSE);
+
+  va_start (args, first_field_id);
+  ret = gst_structure_id_get_valist (structure, first_field_id, args);
+  va_end (args);
+
+  return ret;
+}
index ecefea2..aa3aa5d 100644 (file)
@@ -132,6 +132,21 @@ void                    gst_structure_id_set_valist         (GstStructure
                                                            GQuark                   fieldname,
                                                            va_list varargs);
 
+gboolean                gst_structure_get_valist           (GstStructure            *structure,
+                                                            const char              *first_fieldname,
+                                                            va_list                  args);
+
+gboolean                gst_structure_get                  (GstStructure            *structure,
+                                                            const char              *first_fieldname,
+                                                            ...) G_GNUC_NULL_TERMINATED;
+
+gboolean                gst_structure_id_get_valist        (GstStructure            *structure,
+                                                            GQuark                   first_field_id,
+                                                            va_list                  args);
+
+gboolean                gst_structure_id_get               (GstStructure            *structure,
+                                                            GQuark                   first_field_id,
+                                                            ...) G_GNUC_NULL_TERMINATED;
 
 G_CONST_RETURN GValue * gst_structure_id_get_value         (const GstStructure      *structure,
                                                            GQuark                   field);
index 3d84057..bd126cf 100644 (file)
@@ -433,6 +433,108 @@ GST_START_TEST (test_empty_string_fields)
 
 GST_END_TEST;
 
+GST_START_TEST (test_vararg_getters)
+{
+  GstStructure *s;
+  GstBuffer *buf, *buf2;
+  gboolean ret;
+  GstCaps *caps, *caps2;
+  gdouble d;
+  gint64 i64;
+  gchar *c;
+  gint i, num, denom;
+
+  buf = gst_buffer_new_and_alloc (3);
+  GST_BUFFER_DATA (buf)[0] = 0xf0;
+  GST_BUFFER_DATA (buf)[1] = 0x66;
+  GST_BUFFER_DATA (buf)[2] = 0x0d;
+
+  caps = gst_caps_new_simple ("video/x-foo", NULL);
+
+  s = gst_structure_new ("test", "int", G_TYPE_INT, 12345678, "string",
+      G_TYPE_STRING, "Hello World!", "buf", GST_TYPE_BUFFER, buf, "caps",
+      GST_TYPE_CAPS, caps, "int64", G_TYPE_INT64, G_GINT64_CONSTANT (-99),
+      "double", G_TYPE_DOUBLE, G_MAXDOUBLE, "frag", GST_TYPE_FRACTION, 39, 14,
+      NULL);
+
+  /* first the plain one */
+  ret = gst_structure_get (s, "double", G_TYPE_DOUBLE, &d, "string",
+      G_TYPE_STRING, &c, "caps", GST_TYPE_CAPS, &caps2, "buf",
+      GST_TYPE_BUFFER, &buf2, "frag", GST_TYPE_FRACTION, &num, &denom, "int",
+      G_TYPE_INT, &i, "int64", G_TYPE_INT64, &i64, NULL);
+
+  fail_unless (ret);
+  fail_unless_equals_string (c, "Hello World!");
+  fail_unless_equals_int (i, 12345678);
+  fail_unless_equals_float (d, G_MAXDOUBLE);
+  fail_unless_equals_int (num, 39);
+  fail_unless_equals_int (denom, 14);
+  fail_unless (i64 == -99);
+  fail_unless (caps == caps2);
+  fail_unless (buf == buf2);
+
+  /* expected failures */
+  ASSERT_CRITICAL (gst_structure_get (s, NULL, G_TYPE_INT, &i, NULL));
+  fail_if (gst_structure_get (s, "int", G_TYPE_INT, &i, "double",
+          G_TYPE_FLOAT, &d, NULL));
+  fail_if (gst_structure_get (s, "int", G_TYPE_INT, &i, "dooble",
+          G_TYPE_DOUBLE, &d, NULL));
+
+  g_free (c);
+  c = NULL;
+  gst_caps_unref (caps2);
+  caps2 = NULL;
+  gst_buffer_unref (buf2);
+  buf2 = NULL;
+
+  /* and now the _id variant */
+  ret = gst_structure_id_get (s, g_quark_from_static_string ("double"),
+      G_TYPE_DOUBLE, &d, g_quark_from_static_string ("string"), G_TYPE_STRING,
+      &c, g_quark_from_static_string ("caps"), GST_TYPE_CAPS, &caps2,
+      g_quark_from_static_string ("buf"), GST_TYPE_BUFFER, &buf2,
+      g_quark_from_static_string ("int"), G_TYPE_INT, &i,
+      g_quark_from_static_string ("int64"), G_TYPE_INT64, &i64, NULL);
+
+  fail_unless (ret);
+  fail_unless_equals_string (c, "Hello World!");
+  fail_unless_equals_int (i, 12345678);
+  fail_unless_equals_float (d, G_MAXDOUBLE);
+  fail_unless (i64 == -99);
+  fail_unless (caps == caps2);
+  fail_unless (buf == buf2);
+
+  /* expected failures */
+  ASSERT_CRITICAL (gst_structure_get (s, 0, G_TYPE_INT, &i, NULL));
+  fail_if (gst_structure_id_get (s, g_quark_from_static_string ("int"),
+          G_TYPE_INT, &i, g_quark_from_static_string ("double"), G_TYPE_FLOAT,
+          &d, NULL));
+  fail_if (gst_structure_id_get (s, g_quark_from_static_string ("int"),
+          G_TYPE_INT, &i, g_quark_from_static_string ("dooble"), G_TYPE_DOUBLE,
+          &d, NULL));
+
+  g_free (c);
+  gst_caps_unref (caps2);
+  gst_buffer_unref (buf2);
+
+  /* finally make sure NULL as return location is handled gracefully */
+  ret = gst_structure_get (s, "double", G_TYPE_DOUBLE, NULL, "string",
+      G_TYPE_STRING, NULL, "caps", GST_TYPE_CAPS, NULL, "buf",
+      GST_TYPE_BUFFER, NULL, "int", G_TYPE_INT, &i, "frag", GST_TYPE_FRACTION,
+      NULL, NULL, "int64", G_TYPE_INT64, &i64, NULL);
+
+  ASSERT_WARNING (gst_structure_get (s, "frag", GST_TYPE_FRACTION, NULL,
+          &denom, NULL));
+  ASSERT_WARNING (gst_structure_get (s, "frag", GST_TYPE_FRACTION, &num,
+          NULL, NULL));
+
+  /* clean up */
+  gst_caps_unref (caps);
+  gst_buffer_unref (buf);
+  gst_structure_free (s);
+}
+
+GST_END_TEST;
+
 static Suite *
 gst_structure_suite (void)
 {
@@ -451,6 +553,7 @@ gst_structure_suite (void)
   tcase_add_test (tc_chain, test_structure_nested);
   tcase_add_test (tc_chain, test_structure_nested_from_and_to_string);
   tcase_add_test (tc_chain, test_empty_string_fields);
+  tcase_add_test (tc_chain, test_vararg_getters);
   return s;
 }
 
index 006ad95..7abab52 100644 (file)
@@ -866,6 +866,7 @@ EXPORTS
        gst_structure_foreach
        gst_structure_free
        gst_structure_from_string
+       gst_structure_get
        gst_structure_get_boolean
        gst_structure_get_clock_time
        gst_structure_get_date
@@ -880,11 +881,14 @@ EXPORTS
        gst_structure_get_string
        gst_structure_get_type
        gst_structure_get_uint
+       gst_structure_get_valist
        gst_structure_get_value
        gst_structure_has_field
        gst_structure_has_field_typed
        gst_structure_has_name
        gst_structure_id_empty_new
+       gst_structure_id_get
+       gst_structure_id_get_valist
        gst_structure_id_get_value
        gst_structure_id_new
        gst_structure_id_set