DMP Photo Booth: an Experimental GHashTable Transplant
DMP Photo Booth has had a problem; it has been battered by the club of poor design choices. Deep in the bowels of user_interface.c, there was a terrible cancer. There was a struct called
dmp_pb_ui_cb_user_data that started life as an innocent little struct to be passed into GTK callbacks. Since the same pointer must be used for all callbacks to pass user data around, this was my solution. The only problem? Scalability.
At first, there was one member to worry about. One member to initialize, one member to free. Then there were 2. Then 4. Then 8. Things began to take a turn for the worse as my user data registration function ballooned to over 300 lines. Dozens of members to track, to new and destroy. Fearing that my poor user_interface.c file would end up on The Daily WTF I prepared for surgery. The cancer must go.
Easier said than done, right? It turns out, not so much.
GHashTable to the Rescue
Once again, GLib has the answer. After some thought, GLib’s Hash Table implementation offers the solution.
A hash table is a collection that associates a key with a value. You can pass in an integer, a string, or any other arbitrary type as a key, and get its associated value. In my case, I have ready-made keys: the Glade widget name. I can use these names as keys and associate them with their respective components as they are pulled out of the GtkBuilder. The filled hash can then be passed in as the user data pointer to gtk callbacks.
Let’s go over some of the details that make this is a good choice for DMP Photo Booth:
This is, of course, the GHashTable constructor. Let’s examine an example, straight out of the new implementation:
dmp_pb_user_data = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, (GDestroyNotify) gtk_widget_destroy );
This constructor takes 4 arguments: a hashing function, an equality function, a key destruction function, and a value destruction function. The hashing function is probably the most important of these. Hash tables generate a hash based on the key to find the value, this function implements the hash on a given type. GLib provides 3 ready-made hashing functions,
g_str_hash (for char arrays),
g_int_hash (for integers), and
g_direct_hash (for void pointers). You can implement your own if you like, just make sure it’s fast. This function is called on all lookups, a slow hash function will destroy your program’s performance.
Next is the equality function. This function is used to test keys for equality.
Finally, we have the key and value destruction functions. These functions are responsible for ensuring memory is cleaned up. These functions will be called on destruction of the GHashTable, ensuring all memory is cleaned up properly. This is the reason we are using
g_hash_table_new_full, and not
g_hash_table_new. Without these functions, you have to manually empty the hash table and free all values if you want to avoid a memory leak.
Next, is our lookup function. This function takes a pointer to a GHashTable, and a key. A pointer to the key’s value is returned, or
NULL if the key does not exist. Pretty straight forward.
Favorable. The new GHashTable is humming along, and
dmp_pb_ui_register_user_data has shed hundred of lines. With a little diet and excercise, the patient should make a full recovery.