diff mbox

[v4,1/7] qom: introduce ObjectPropertyIterator struct for iteration

Message ID 1444739866-14798-2-git-send-email-berrange@redhat.com
State New
Headers show

Commit Message

Daniel P. Berrangé Oct. 13, 2015, 12:37 p.m. UTC
Some users of QOM need to be able to iterate over properties
defined against an object instance. Currently they are just
directly using the QTAIL macros against the object properties
data structure.

This is bad because it exposes them to changes in the data
structure used to store properties, as well as changes in
functionality such as ability to register properties against
the class.

This provides an ObjectPropertyIterator struct which will
insulate the callers from the particular data structure
used to store properties. It can be used thus

  ObjectProperty *prop;
  ObjectProperty *iter;

  iter = object_property_iter_init(obj);
  while ((prop = object_property_iter_next(iter))) {
      ... do something with prop ...
  }
  object_property_iter_free(iter);

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/qom/object.h       | 50 ++++++++++++++++++++++++++++++++++++++++++++++
 qom/object.c               | 31 ++++++++++++++++++++++++++++
 tests/check-qom-proplist.c | 46 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 127 insertions(+)

Comments

Andreas Färber Nov. 5, 2015, 4:59 p.m. UTC | #1
Am 13.10.2015 um 14:37 schrieb Daniel P. Berrange:
> Some users of QOM need to be able to iterate over properties
> defined against an object instance. Currently they are just
> directly using the QTAIL macros against the object properties
> data structure.
> 
> This is bad because it exposes them to changes in the data
> structure used to store properties, as well as changes in
> functionality such as ability to register properties against
> the class.
> 
> This provides an ObjectPropertyIterator struct which will
> insulate the callers from the particular data structure
> used to store properties. It can be used thus
> 
>   ObjectProperty *prop;
>   ObjectProperty *iter;
> 
>   iter = object_property_iter_init(obj);
>   while ((prop = object_property_iter_next(iter))) {
>       ... do something with prop ...
>   }
>   object_property_iter_free(iter);
> 
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  include/qom/object.h       | 50 ++++++++++++++++++++++++++++++++++++++++++++++
>  qom/object.c               | 31 ++++++++++++++++++++++++++++
>  tests/check-qom-proplist.c | 46 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 127 insertions(+)
> 
> diff --git a/include/qom/object.h b/include/qom/object.h
> index be7280c..761ffec 100644
> --- a/include/qom/object.h
> +++ b/include/qom/object.h
> @@ -960,6 +960,56 @@ void object_property_del(Object *obj, const char *name, Error **errp);
>  ObjectProperty *object_property_find(Object *obj, const char *name,
>                                       Error **errp);
>  
> +typedef struct ObjectPropertyIterator ObjectPropertyIterator;
> +
> +/**
> + * object_property_iter_init:
> + * @obj: the object
> + *
> + * Initializes an iterator for traversing all properties
> + * registered against an object instance.
> + *
> + * It is forbidden to modify the property list while iterating,
> + * whether removing or adding properties.
> + *
> + * Typical usage pattern would be
> + *
> + * <example>
> + *   <title>Using object property iterators</title>
> + *   <programlisting>
> + *   ObjectProperty *prop;
> + *   ObjectProperty *iter;

Shouldn't this be ObjectPropertyIterator?

> + *
> + *   iter = object_property_iter_init(obj);
> + *   while ((prop = object_property_iter_next(iter))) {
> + *     ... do something with prop ...
> + *   }
> + *   object_property_iter_free(iter);
> + *   </programlisting>
> + * </example>
> + *
> + * Returns the new iterator

Returns:

> + */
> +ObjectPropertyIterator *object_property_iter_init(Object *obj);
> +
> +

Intentionally two lines here?

> +/**
> + * object_property_iter_free:
> + * @iter: the iterator instance
> + *
> + * Release any resources associated with the iterator

Releases ... full stop.

> + */
> +void object_property_iter_free(ObjectPropertyIterator *iter);
> +
> +/**
> + * object_property_iter_next:
> + * @iter: the iterator instance
> + *
> + * Returns the next property, or NULL when all properties

%NULL

> + * have been traversed.
> + */
> +ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter);
> +
>  void object_unparent(Object *obj);
>  
>  /**
> diff --git a/qom/object.c b/qom/object.c
> index 4805328..7dace59 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -67,6 +67,10 @@ struct TypeImpl
>      InterfaceImpl interfaces[MAX_INTERFACES];
>  };
>  
> +struct ObjectPropertyIterator {
> +    ObjectProperty *next;
> +};
> +
>  static Type type_interface;
>  
>  static GHashTable *type_table_get(void)
> @@ -917,6 +921,33 @@ ObjectProperty *object_property_find(Object *obj, const char *name,
>      return NULL;
>  }
>  
> +ObjectPropertyIterator *object_property_iter_init(Object *obj)
> +{
> +    ObjectPropertyIterator *ret = g_new0(ObjectPropertyIterator, 1);
> +    ret->next = QTAILQ_FIRST(&obj->properties);
> +    return ret;
> +}
> +
> +
> +void object_property_iter_free(ObjectPropertyIterator *iter)
> +{
> +    if (!iter) {
> +        return;
> +    }
> +    g_free(iter);
> +}
> +
> +
> +ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter)
> +{
> +    ObjectProperty *ret = iter->next;
> +    if (ret) {
> +        iter->next = QTAILQ_NEXT(iter->next, node);
> +    }
> +    return ret;
> +}
> +
> +

?

>  void object_property_del(Object *obj, const char *name, Error **errp)
>  {
>      ObjectProperty *prop = object_property_find(obj, name, errp);
> diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
> index 7400b1f..1be8b9e 100644
> --- a/tests/check-qom-proplist.c
> +++ b/tests/check-qom-proplist.c
> @@ -283,6 +283,51 @@ static void test_dummy_getenum(void)
>                                     &err);
>      g_assert(err != NULL);
>      error_free(err);
> +
> +    object_unparent(OBJECT(dobj));
> +}
> +
> +
> +static void test_dummy_iterator(void)
> +{
> +    Object *parent = object_get_objects_root();
> +    DummyObject *dobj = DUMMY_OBJECT(
> +        object_new_with_props(TYPE_DUMMY,
> +                              parent,
> +                              "dummy0",
> +                              &error_abort,
> +                              "bv", "yes",
> +                              "sv", "Hiss hiss hiss",
> +                              "av", "platypus",
> +                              NULL));
> +
> +    ObjectProperty *prop;
> +    ObjectPropertyIterator *iter;
> +    bool seenbv = false, seensv = false, seenav = false, seentype;
> +
> +    iter = object_property_iter_init(OBJECT(dobj));
> +    while ((prop = object_property_iter_next(iter))) {
> +        if (g_str_equal(prop->name, "bv")) {
> +            seenbv = true;
> +        } else if (g_str_equal(prop->name, "sv")) {
> +            seensv = true;
> +        } else if (g_str_equal(prop->name, "av")) {
> +            seenav = true;
> +        } else if (g_str_equal(prop->name, "type")) {
> +            /* This prop comes from the base Object class */
> +            seentype = true;
> +        } else {
> +            g_printerr("Found prop '%s'\n", prop->name);
> +            g_assert_not_reached();
> +        }
> +    }
> +    object_property_iter_free(iter);
> +    g_assert(seenbv);
> +    g_assert(seenav);
> +    g_assert(seensv);
> +    g_assert(seentype);
> +
> +    object_unparent(OBJECT(dobj));
>  }
>  
>  
> @@ -297,6 +342,7 @@ int main(int argc, char **argv)
>      g_test_add_func("/qom/proplist/createv", test_dummy_createv);
>      g_test_add_func("/qom/proplist/badenum", test_dummy_badenum);
>      g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
> +    g_test_add_func("/qom/proplist/iterator", test_dummy_iterator);
>  
>      return g_test_run();
>  }

Pavel, note that any of you could've found such issues or at least
provided a Reviewed-by rather than just pinging.

Regards,
Andreas
Markus Armbruster Nov. 17, 2015, 3:25 p.m. UTC | #2
I apologize for the lateness of my review.

"Daniel P. Berrange" <berrange@redhat.com> writes:

> Some users of QOM need to be able to iterate over properties
> defined against an object instance. Currently they are just
> directly using the QTAIL macros against the object properties
> data structure.
>
> This is bad because it exposes them to changes in the data
> structure used to store properties, as well as changes in
> functionality such as ability to register properties against
> the class.
>
> This provides an ObjectPropertyIterator struct which will
> insulate the callers from the particular data structure
> used to store properties. It can be used thus
>
>   ObjectProperty *prop;
>   ObjectProperty *iter;

ObjectPropertyIterator *iter;

>
>   iter = object_property_iter_init(obj);
>   while ((prop = object_property_iter_next(iter))) {
>       ... do something with prop ...
>   }
>   object_property_iter_free(iter);

This is a sane way to do iterators.  It combines allocation and
initialization, thus requires a free.  The name _init() suggests it
doesn't, but that's easy enough to fix.  I can't find precedence for
this way in the tree, however.

Another way is

    ObjectProperty *prop;
    ObjectPropertyIterator iter;

    object_property_iter_init(&iter, obj);
    while ((prop = object_property_iter_next(iter))) {
        ... do something with prop ...
    }
    object_property_iter_fini(iter); // usually not needed

Leaves allocation to the caller, which makes it more flexible.
Automartic iter often suffices, and can't leak.  The _fini() is commonly
empty and left out.

Precedence:
* hbitmap_iter_init() and hbitmap_iter_next()
* g_hash_table_iter_init() and g_hash_table_iter_next(), except the
  latter is a bit different because it returns two values

Yet another one is

    for (prop = object_property_first(obj);
         prop;
         prop = object_property_next(prop)) {
        ... do something with prop ...
    }

This works only when the iterator state is exactly what the _next()
returns.  Happens to be the case often enough.  Here, too, but perhaps
you have reasons to prepare for more complex state.

Precedence:
* qdict_first(), qdict_next()
* vma_first(), vma_next()
* ...

Related: bdrv_next(), blk_next(), ...  These guys omit _first() because
the set to iterate over is implied.

I recommend to pick the precedence you like best for this purpose and
follow it.

> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  include/qom/object.h       | 50 ++++++++++++++++++++++++++++++++++++++++++++++
>  qom/object.c               | 31 ++++++++++++++++++++++++++++
>  tests/check-qom-proplist.c | 46 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 127 insertions(+)
>
> diff --git a/include/qom/object.h b/include/qom/object.h
> index be7280c..761ffec 100644
> --- a/include/qom/object.h
> +++ b/include/qom/object.h
> @@ -960,6 +960,56 @@ void object_property_del(Object *obj, const char *name, Error **errp);
>  ObjectProperty *object_property_find(Object *obj, const char *name,
>                                       Error **errp);
>  
> +typedef struct ObjectPropertyIterator ObjectPropertyIterator;
> +
> +/**
> + * object_property_iter_init:
> + * @obj: the object
> + *
> + * Initializes an iterator for traversing all properties
> + * registered against an object instance.
> + *
> + * It is forbidden to modify the property list while iterating,
> + * whether removing or adding properties.
> + *
> + * Typical usage pattern would be
> + *
> + * <example>
> + *   <title>Using object property iterators</title>
> + *   <programlisting>
> + *   ObjectProperty *prop;
> + *   ObjectProperty *iter;
> + *
> + *   iter = object_property_iter_init(obj);
> + *   while ((prop = object_property_iter_next(iter))) {
> + *     ... do something with prop ...
> + *   }
> + *   object_property_iter_free(iter);
> + *   </programlisting>
> + * </example>
> + *
> + * Returns the new iterator
> + */
> +ObjectPropertyIterator *object_property_iter_init(Object *obj);
> +
> +
> +/**
> + * object_property_iter_free:
> + * @iter: the iterator instance
> + *
> + * Release any resources associated with the iterator
> + */
> +void object_property_iter_free(ObjectPropertyIterator *iter);
> +
> +/**
> + * object_property_iter_next:
> + * @iter: the iterator instance
> + *
> + * Returns the next property, or NULL when all properties
> + * have been traversed.
> + */
> +ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter);
> +
>  void object_unparent(Object *obj);
>  
>  /**
> diff --git a/qom/object.c b/qom/object.c
> index 4805328..7dace59 100644
> --- a/qom/object.c
> +++ b/qom/object.c
> @@ -67,6 +67,10 @@ struct TypeImpl
>      InterfaceImpl interfaces[MAX_INTERFACES];
>  };
>  
> +struct ObjectPropertyIterator {
> +    ObjectProperty *next;
> +};
> +
>  static Type type_interface;
>  
>  static GHashTable *type_table_get(void)
> @@ -917,6 +921,33 @@ ObjectProperty *object_property_find(Object *obj, const char *name,
>      return NULL;
>  }
>  
> +ObjectPropertyIterator *object_property_iter_init(Object *obj)
> +{
> +    ObjectPropertyIterator *ret = g_new0(ObjectPropertyIterator, 1);
> +    ret->next = QTAILQ_FIRST(&obj->properties);
> +    return ret;
> +}
> +
> +
> +void object_property_iter_free(ObjectPropertyIterator *iter)
> +{
> +    if (!iter) {
> +        return;
> +    }
> +    g_free(iter);

g_free(NULL) is perfectly safe; please drop the conditional.

> +}
> +
> +
> +ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter)
> +{
> +    ObjectProperty *ret = iter->next;
> +    if (ret) {
> +        iter->next = QTAILQ_NEXT(iter->next, node);
> +    }
> +    return ret;
> +}
> +
> +
>  void object_property_del(Object *obj, const char *name, Error **errp)
>  {
>      ObjectProperty *prop = object_property_find(obj, name, errp);
[...]
Daniel P. Berrangé Nov. 17, 2015, 3:27 p.m. UTC | #3
On Tue, Nov 17, 2015 at 04:25:22PM +0100, Markus Armbruster wrote:
> I apologize for the lateness of my review.

> > +void object_property_iter_free(ObjectPropertyIterator *iter)
> > +{
> > +    if (!iter) {
> > +        return;
> > +    }
> > +    g_free(iter);
> 
> g_free(NULL) is perfectly safe; please drop the conditional.

The next patch in the series has to free some fields in 'iter'
so the check for NULL upfront is not redundant.

Regards,
Daniel
Markus Armbruster Nov. 17, 2015, 3:35 p.m. UTC | #4
"Daniel P. Berrange" <berrange@redhat.com> writes:

> On Tue, Nov 17, 2015 at 04:25:22PM +0100, Markus Armbruster wrote:
>> I apologize for the lateness of my review.
>
>> > +void object_property_iter_free(ObjectPropertyIterator *iter)
>> > +{
>> > +    if (!iter) {
>> > +        return;
>> > +    }
>> > +    g_free(iter);
>> 
>> g_free(NULL) is perfectly safe; please drop the conditional.
>
> The next patch in the series has to free some fields in 'iter'
> so the check for NULL upfront is not redundant.

I'd add the conditional when it's actually needed, not least to avoid
comments from picky reviewers like myself ;)  Anyway, not worth a respin
by itself.

However, I'd very much prefer the iterators to follow precedence in the
tree.
diff mbox

Patch

diff --git a/include/qom/object.h b/include/qom/object.h
index be7280c..761ffec 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -960,6 +960,56 @@  void object_property_del(Object *obj, const char *name, Error **errp);
 ObjectProperty *object_property_find(Object *obj, const char *name,
                                      Error **errp);
 
+typedef struct ObjectPropertyIterator ObjectPropertyIterator;
+
+/**
+ * object_property_iter_init:
+ * @obj: the object
+ *
+ * Initializes an iterator for traversing all properties
+ * registered against an object instance.
+ *
+ * It is forbidden to modify the property list while iterating,
+ * whether removing or adding properties.
+ *
+ * Typical usage pattern would be
+ *
+ * <example>
+ *   <title>Using object property iterators</title>
+ *   <programlisting>
+ *   ObjectProperty *prop;
+ *   ObjectProperty *iter;
+ *
+ *   iter = object_property_iter_init(obj);
+ *   while ((prop = object_property_iter_next(iter))) {
+ *     ... do something with prop ...
+ *   }
+ *   object_property_iter_free(iter);
+ *   </programlisting>
+ * </example>
+ *
+ * Returns the new iterator
+ */
+ObjectPropertyIterator *object_property_iter_init(Object *obj);
+
+
+/**
+ * object_property_iter_free:
+ * @iter: the iterator instance
+ *
+ * Release any resources associated with the iterator
+ */
+void object_property_iter_free(ObjectPropertyIterator *iter);
+
+/**
+ * object_property_iter_next:
+ * @iter: the iterator instance
+ *
+ * Returns the next property, or NULL when all properties
+ * have been traversed.
+ */
+ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter);
+
 void object_unparent(Object *obj);
 
 /**
diff --git a/qom/object.c b/qom/object.c
index 4805328..7dace59 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -67,6 +67,10 @@  struct TypeImpl
     InterfaceImpl interfaces[MAX_INTERFACES];
 };
 
+struct ObjectPropertyIterator {
+    ObjectProperty *next;
+};
+
 static Type type_interface;
 
 static GHashTable *type_table_get(void)
@@ -917,6 +921,33 @@  ObjectProperty *object_property_find(Object *obj, const char *name,
     return NULL;
 }
 
+ObjectPropertyIterator *object_property_iter_init(Object *obj)
+{
+    ObjectPropertyIterator *ret = g_new0(ObjectPropertyIterator, 1);
+    ret->next = QTAILQ_FIRST(&obj->properties);
+    return ret;
+}
+
+
+void object_property_iter_free(ObjectPropertyIterator *iter)
+{
+    if (!iter) {
+        return;
+    }
+    g_free(iter);
+}
+
+
+ObjectProperty *object_property_iter_next(ObjectPropertyIterator *iter)
+{
+    ObjectProperty *ret = iter->next;
+    if (ret) {
+        iter->next = QTAILQ_NEXT(iter->next, node);
+    }
+    return ret;
+}
+
+
 void object_property_del(Object *obj, const char *name, Error **errp)
 {
     ObjectProperty *prop = object_property_find(obj, name, errp);
diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
index 7400b1f..1be8b9e 100644
--- a/tests/check-qom-proplist.c
+++ b/tests/check-qom-proplist.c
@@ -283,6 +283,51 @@  static void test_dummy_getenum(void)
                                    &err);
     g_assert(err != NULL);
     error_free(err);
+
+    object_unparent(OBJECT(dobj));
+}
+
+
+static void test_dummy_iterator(void)
+{
+    Object *parent = object_get_objects_root();
+    DummyObject *dobj = DUMMY_OBJECT(
+        object_new_with_props(TYPE_DUMMY,
+                              parent,
+                              "dummy0",
+                              &error_abort,
+                              "bv", "yes",
+                              "sv", "Hiss hiss hiss",
+                              "av", "platypus",
+                              NULL));
+
+    ObjectProperty *prop;
+    ObjectPropertyIterator *iter;
+    bool seenbv = false, seensv = false, seenav = false, seentype;
+
+    iter = object_property_iter_init(OBJECT(dobj));
+    while ((prop = object_property_iter_next(iter))) {
+        if (g_str_equal(prop->name, "bv")) {
+            seenbv = true;
+        } else if (g_str_equal(prop->name, "sv")) {
+            seensv = true;
+        } else if (g_str_equal(prop->name, "av")) {
+            seenav = true;
+        } else if (g_str_equal(prop->name, "type")) {
+            /* This prop comes from the base Object class */
+            seentype = true;
+        } else {
+            g_printerr("Found prop '%s'\n", prop->name);
+            g_assert_not_reached();
+        }
+    }
+    object_property_iter_free(iter);
+    g_assert(seenbv);
+    g_assert(seenav);
+    g_assert(seensv);
+    g_assert(seentype);
+
+    object_unparent(OBJECT(dobj));
 }
 
 
@@ -297,6 +342,7 @@  int main(int argc, char **argv)
     g_test_add_func("/qom/proplist/createv", test_dummy_createv);
     g_test_add_func("/qom/proplist/badenum", test_dummy_badenum);
     g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
+    g_test_add_func("/qom/proplist/iterator", test_dummy_iterator);
 
     return g_test_run();
 }