goto and you
by eanx on Dec.15, 2008, under Open Source
Like most people in the open source community, I want to try to give back something to the community; be it in the form of a code refactoring that greatly simplifies or modularizes something to just learning more about how things work. In my quest to learn more about programming and computer science in general, I’ve been intrigued with the use of the goto construct in the Linux kernel and open source software in general.
In the Ubuntu-distributed source for Linux 2.6.24, there are 50,295 occurrences of goto in the kernel code:
eanx@crocostimpy:~$ grep -R "goto" /usr/src/linux-source-2.6.24 | wc -l 50295
Edsger Dijkstra who is perhaps best known for his shortest-path algorithm, was a vehement opposer to the use of the goto construct made; A Case Against The GO TO Statement.
This question is frequently a topic found on Linux kernel hacking mailing lists. Linus himself offers a pretty good explanation of why you they’re good form for certain applications. One good reason he gives is breaking out of the middle of deeply nested conditional statements and for very rarely used exceptions. Remember, C doesn’t have a try-catch construct. Another reason given is that it enhances readability and therefore maintainability.
That said, I was curious about a few things. So I recently downloaded the source for the Pidgin IM client, version 2.5.2.:
eanx@crocostimpy:~$ grep -R "goto" pidgin-2.5.2 | wc -l 285
There are 285 occurrences in Pidgin and so naturally, I got a little curious to see if I could maybe refactor a few of them out of the program altogether and I discovered this amusing comment in ./libpurple/dbus-server.c in purple_dbus_iter_has_table(). I immediately thought, this is one of the joys of open source software; reading the comments!
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 | GHashTable * purple_dbus_iter_hash_table(DBusMessageIter *iter, DBusError *error) { GHashTable *hash; /* we do not need to destroy strings because they are part of the message */ hash = g_hash_table_new(g_str_hash, g_str_equal); do { char *key, *value; DBusMessageIter subiter; if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_DICT_ENTRY) goto error; /* With all due respect to Dijkstra, * this goto is for exception * handling, and it is ok because it * avoids duplication of the code * responsible for destroying the hash * table. Exceptional instructions * for exceptional situations. */ dbus_message_iter_recurse(iter, &subiter); if (!purple_dbus_message_iter_get_args(&subiter, error, DBUS_TYPE_STRING, &key, DBUS_TYPE_STRING, &value, DBUS_TYPE_INVALID)) goto error; /* same here */ g_hash_table_insert(hash, key, value); } while (dbus_message_iter_next(iter)); return hash; error: g_hash_table_destroy(hash); return NULL; } |
When all is said and done, it is a useful construct if you understand the consequences and tradeoffs. Like everything else in computer science and software engineering, the dos and do nots should be studied and understood.