Given the code snippet:
#include <glib.h>
#include <stdio.h>
void print_city(gpointer value, gpointer data)
{
printf("%s, ", value);
}
void print(gpointer key, gpointer value, gpointer data)
{
printf("Here are some cities in %s: ", key);
g_slist_foreach((GSList *)value, (GFunc)print_city, NULL);
printf("\n");
}
void destroy(gpointer key, gpointer value, gpointer data)
{
printf("Freeing a GSList, first item is %s\n", ((GSList*)value)->data);
g_slist_free(value);
}
int main(int argc, char *argv[])
{
GHashTable *hash = g_hash_table_new(g_str_hash, g_str_equal);
g_hash_table_insert(hash,
"Virginia",
g_slist_append(g_hash_table_lookup(hash, "Virginia"), "Richmond")
);
g_hash_table_insert(hash,
"Virginia",
g_slist_append(g_hash_table_lookup(hash, "Virginia"), "Keysville")
);
g_hash_table_insert(hash,
"Texas",
g_slist_append(g_hash_table_lookup(hash, "Texas"), "Houston")
);
g_hash_table_insert(hash,
"Texas",
g_slist_append(g_hash_table_lookup(hash, "Texas"), "Austin")
);
g_hash_table_foreach(hash, print, NULL);
g_hash_table_foreach(hash, destroy, NULL);
g_hash_table_destroy(hash);
return 0;
}
We can notice that the lines in main
function that calls g_hash_table_insert
, looks a bit convoluted, repeating itself, hence I was looking code alternatives to simplify the value parameter. With that in mind, my first attempt to change the code is the following (comments added)
GSList *list = NULL;
// Adding the first element to `list`
// list contains the pointer to the GSList head
list = g_slist_append(list, "Austin");
// Inserting the list (an address to a GSLIST) into
// the value of "Texas" key.
g_hash_table_insert(hash, "Texas", list);
// Retrieves the content of "Texas" key.
// (That is right now the pointer to the GSList head)
// and stores into value pointer
list = g_hash_table_lookup(hash, "Texas");
// Add the string "Houston" to the GSList and stores
// the new head at `value` to keep track of head.
list = g_slist_append(list, "Houston");
// Updates the 'key' Texas with list.
g_hash_table_insert(hash, "Texas", list);
Does not looks much better. I have also added a few comments in code to describe my understanding.
Question 1: Does my understanding (expressed in comments) are correct?
Question 2: Keeping the code consistency, is there any way to improve the code above?
I noticed that I could remove all calls to g_hash_table_insert
after the first one, maybe sacrificing some consistency. The code in this case would looks like this
GSList *list = NULL;
list = g_slist_append(list, "Austin");
g_hash_table_insert(hash, "Texas", list);
list = g_hash_table_lookup(hash, "Texas");
list = g_slist_append(list, "Houston");
Question 3: How this change affects the consistency?
Question 4: Which behaviors could emerge from this change?
Anyhow, given this previous snippet I tried to make things more "organized" hence I reached the follow snippet to update the list in GHashTable:
GSList *list = NULL;
g_hash_table_insert(hash, "Texas", list);
list = g_hash_table_lookup(hash, "Texas");
list = g_slist_append(list, "Austin");
list = g_slist_append(list, "Houston");
The code above seems more organized, but fails completely in work (even triggering segfault on free
calls). I could not understand why this last snippet fails.
Question 5: Why the segfault is happening?
Question 6: Is there a less convoluted way to handle GSLists
in GHashTables
that not uses the initial construction?
GSList *list = NULL;
g_hash_table_insert(hash, "Texas", list);
list = g_hash_table_lookup(hash, "Texas");
list = g_slist_append(list, "Austin");
list = g_slist_append(list, "Houston");
Your hashtable only ever contains NULL here. Even the previous example looks iffy and is not actually guaranteed to work: anytime you modify the list, the head pointer may change.
The simplest way to do what you seem to want is to build the list first, then add it to the hashtable:
GSList *list = g_slist_append(NULL, "Austin");
list = g_slist_append(list, "Houston");
g_hash_table_insert(hash, "Texas", list);
But any time you want to modify the list you will have to re-insert it to the hashtable... I would suggest reconsidering your data structure choices: Linked lists are not really great for anything (and the GLib implementation is especially annoying as you found out). GSequence, GArray (or even GHashTable with g_hash_table_add()) might be suitable alternatives within GLib.