diff --git a/glnx-macros.h b/glnx-macros.h index 87c25cadb4aa0b19499fb4e40d31f90461843ad5..cee068d4fb41bf2ca51d29105901b2666432a639 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 d45c5cf8da201d6768b9464b0fe9dcdfa23f0443..2a115fca66119bc5536d2cc916bcc6dc871a2f3d 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(); }