summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Lebon <jlebon@redhat.com>2017-06-14 17:00:25 -0400
committerJonathan Lebon <jlebon@redhat.com>2017-06-17 16:26:05 -0400
commitcaa51ac24ffcdffcb610bc6ccc9da964d4be74ee (patch)
tree1d8b37068a54b3b2e83599c905cdad43d0e2a2d4
parente8b7d8f60ca4918aee61abe22dea959d77f4f189 (diff)
downloadlibglnx-caa51ac24ffcdffcb610bc6ccc9da964d4be74ee.tar.gz
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.
-rw-r--r--glnx-macros.h58
-rw-r--r--tests/test-libglnx-macros.c53
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();
}