Skip to content
Snippets Groups Projects
Commit caa51ac2 authored by Jonathan Lebon's avatar Jonathan Lebon
Browse files

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.
parent e8b7d8f6
No related branches found
No related tags found
No related merge requests found
......@@ -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
......@@ -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();
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment