[libglnx] glnx-macros.h: add GLNX_HASH_TABLE_FOREACH macros



commit caa51ac24ffcdffcb610bc6ccc9da964d4be74ee
Author: Jonathan Lebon <jlebon redhat com>
Date:   Wed Jun 14 17:00:25 2017 -0400

    glnx-macros.h: add GLNX_HASH_TABLE_FOREACH macros
    
    These macros make it much easier to iterate over a GHashTable. It takes
    care of initializing an iterator and casting keys and values to their
    proper types.
    
    See the example usage in the docstring for more info.

 glnx-macros.h               |   58 +++++++++++++++++++++++++++++++++++++++++++
 tests/test-libglnx-macros.c |   53 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 111 insertions(+), 0 deletions(-)
---
diff --git a/glnx-macros.h b/glnx-macros.h
index 87c25ca..cee068d 100644
--- a/glnx-macros.h
+++ b/glnx-macros.h
@@ -111,5 +111,63 @@ G_BEGIN_DECLS
 
 #endif /* ifndef G_IN_SET */
 
+#define _GLNX_CONCAT(a, b)  a##b
+#define _GLNX_CONCAT_INDIRECT(a, b) _GLNX_CONCAT(a, b)
+#define _GLNX_MAKE_ANONYMOUS(a) _GLNX_CONCAT_INDIRECT(a, __COUNTER__)
+
+#define _GLNX_HASH_TABLE_FOREACH_IMPL_KV(guard, ht, it, kt, k, vt, v)          \
+    gboolean guard = TRUE;                                                     \
+    for (GHashTableIter it;                                                    \
+         guard && ({ g_hash_table_iter_init (&it, ht), TRUE; });               \
+         guard = FALSE)                                                        \
+            for (kt k; guard; guard = FALSE)                                   \
+                for (vt v; g_hash_table_iter_next (&it, (gpointer)&k, (gpointer)&v);)
+
+
+/* Cleaner method to iterate over a GHashTable. I.e. rather than
+ *
+ *   gpointer k, v;
+ *   GHashTableIter it;
+ *   g_hash_table_iter_init (&it, table);
+ *   while (g_hash_table_iter_next (&it, &k, &v))
+ *     {
+ *       const char *str = k;
+ *       GPtrArray *arr = v;
+ *       ...
+ *     }
+ *
+ * you can simply do
+ *
+ *   GLNX_HASH_TABLE_FOREACH_IT (table, it, const char*, str, GPtrArray*, arr)
+ *     {
+ *       ...
+ *     }
+ *
+ * All variables are scoped within the loop. You may use the `it` variable as
+ * usual, e.g. to remove an element using g_hash_table_iter_remove(&it). There
+ * are shorter variants for the more common cases where you do not need access
+ * to the iterator or to values:
+ *
+ *   GLNX_HASH_TABLE_FOREACH (table, const char*, str) { ... }
+ *   GLNX_HASH_TABLE_FOREACH_KV (table, const char*, str, MyData*, data) { ... }
+ *
+ */
+#define GLNX_HASH_TABLE_FOREACH_IT(ht, it, kt, k, vt, v) \
+    _GLNX_HASH_TABLE_FOREACH_IMPL_KV( \
+         _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, it, kt, k, vt, v)
+
+/* Variant of GLNX_HASH_TABLE_FOREACH without having to specify an iterator. An
+ * anonymous iterator will be created. */
+#define GLNX_HASH_TABLE_FOREACH_KV(ht, kt, k, vt, v) \
+    _GLNX_HASH_TABLE_FOREACH_IMPL_KV( \
+         _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, \
+         _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_it_), kt, k, vt, v)
+
+/* Variant of GLNX_HASH_TABLE_FOREACH_KV which omits unpacking vals. */
+#define GLNX_HASH_TABLE_FOREACH(ht, kt, k) \
+    _GLNX_HASH_TABLE_FOREACH_IMPL_KV( \
+         _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_guard_), ht, \
+         _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_it_), kt, k, \
+         gpointer, _GLNX_MAKE_ANONYMOUS(_glnx_ht_iter_v_))
 
 G_END_DECLS
diff --git a/tests/test-libglnx-macros.c b/tests/test-libglnx-macros.c
index d45c5cf..2a115fc 100644
--- a/tests/test-libglnx-macros.c
+++ b/tests/test-libglnx-macros.c
@@ -40,9 +40,62 @@ test_inset (void)
   g_assert (!G_IN_SET ('y', 'a', 'x', 'c'));
 }
 
+static void
+test_hash_table_foreach (void)
+{
+  /* use var names all different from the macro metavars to ensure proper
+   * substitution */
+  g_autoptr(GHashTable) table = g_hash_table_new (g_str_hash, g_str_equal);
+  const char *keys[] = {"key1", "key2"};
+  const char *vals[] = {"val1", "val2"};
+  g_hash_table_insert (table, (gpointer)keys[0], (gpointer)vals[0]);
+  g_hash_table_insert (table, (gpointer)keys[1], (gpointer)vals[1]);
+
+  guint i = 0;
+  GLNX_HASH_TABLE_FOREACH_IT (table, it, const char*, key, const char*, val)
+    {
+      g_assert_cmpstr (key, ==, keys[i]);
+      g_assert_cmpstr (val, ==, vals[i]);
+      i++;
+    }
+  g_assert_cmpuint (i, ==, 2);
+
+  i = 0;
+  GLNX_HASH_TABLE_FOREACH_IT (table, it, const char*, key, const char*, val)
+    {
+      g_hash_table_iter_remove (&it);
+      break;
+    }
+  g_assert_cmpuint (g_hash_table_size (table), ==, 1);
+
+  g_hash_table_insert (table, (gpointer)keys[1], (gpointer)vals[1]);
+  g_assert_cmpuint (g_hash_table_size (table), ==, 1);
+
+  g_hash_table_insert (table, (gpointer)keys[0], (gpointer)vals[0]);
+  g_assert_cmpuint (g_hash_table_size (table), ==, 2);
+
+  i = 0;
+  GLNX_HASH_TABLE_FOREACH_KV (table, const char*, key, const char*, val)
+    {
+      g_assert_cmpstr (key, ==, keys[i]);
+      g_assert_cmpstr (val, ==, vals[i]);
+      i++;
+    }
+  g_assert_cmpuint (i, ==, 2);
+
+  i = 0;
+  GLNX_HASH_TABLE_FOREACH (table, const char*, key)
+    {
+      g_assert_cmpstr (key, ==, keys[i]);
+      i++;
+    }
+  g_assert_cmpuint (i, ==, 2);
+}
+
 int main (int argc, char **argv)
 {
   g_test_init (&argc, &argv, NULL);
   g_test_add_func ("/inset", test_inset);
+  g_test_add_func ("/hash_table_foreach", test_hash_table_foreach);
   return g_test_run();
 }


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