To make code written for Nautilus look and act in a predictable way, we follow a set of guidelines that specify some details of how we write code. To start, we follow all the guidelines outlined in the GNOME Programming Guidelines.
This document covers both things that are not mentioned in the GNOME Programming Guidelines and things that are mentioned there but need to be re-emphasized because people don't follow them often enough.
I'm just getting started on this document. Feedback is welcome. Eventually I'd like better organization and tons of examples.
- Darin
We use the most-recommended coding style from the GNOME Programming Guidelines. This means that we use the Linux kernel brace style with 8-character tabs (not the GNU brace style), we put spaces before the parentheses that introduce function argument lists, we put the braces that open the block for an if statement on the same line as the if statement (part of Linux kernel brace style).
We prefer to use words rather than acronyms or abbreviations. This means that we name classes with a prefix like Nautilus, not Ntl, for example. And we use variables named result rather than retval or rv.
We strive to have a minimum number of local variables. This makes it easier to move pieces of code around. For more on this, read Refactoring.
We use type casts as little as possible. There are many places in GTK programming where you have to cast to make the program work, but we do whatever we can to avoid this. Also, we prefer to cast data pointers, rather than casting function pointers, since there's so much more to get wrong with function pointer casts.
We use typedefs from <glib.h> for things like guint, guchar and gpointer, but not gint, gchar, or gdouble. Using these gives a false sense of portability. In all three cases, using system calls like printf requires knowing that these are the "real" int, char, and double, so there's no reason to use a typedef that's non-standard unless it's a shorter name or clearer in some way.
We avoid in-band signaling. This means that we avoid using special values to indicate errors, for example. This can lead to subtle bugs when a valid result is misinterpreted as an error, and can make it hard to tell if the code handles errors or not.
We code for clarity first. Other concerns like efficiency are secondary. Sometimes they become more important than clarity, but only once they are proven to be a problem.
We use for loops when they make the code easier to read. The alternative is usually to use a while loop. It's true that "easy to read" is a subjective thing.
We declare local variables at the top of the function. C allows you to declare variables at the top of any scope, like the scope in a control flow construct like an if statement. But putting the declarations there makes it harder to add and delete if statements, since the declarations have to move. So we just put them all at the top of the function, usually in the order that they are used in the function.
We do not initialize local variables in their declarations. C allows you to initialize a local variable when declaring it. But no other code can run before this, because the other statements in a function must be after all the declarations. If there are lines of code initializing the variables in the declarations, it can be harder to change the function around, since code must move down from the declaration if other code needs to run after it. To avoid this, we just don't use the ability to initialize the variable when it's declared.
We always use braces, even for one-statement "blocks". Our consensus is to do things like this:
if (list != NULL) { g_warning ("the list isn't empty"); }
Instead of this:
if (list != NULL) g_warning ("the list isn't empty");
This applies to all control structures: if, while, for, do.
We make each header "stand alone". Our concept with C header files is that each one must be written so it can be included without including another file first. To test that the header files we develop have this property, we always include the corresponding header file first in each C source file. The only exception is the include of <config.h>, which goes first. Here's an example:
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- * * nautilus-icon-factory.c: Class for obtaining icons for files and other objects. * * Copyright (C) 1999, 2000 Red Hat Inc. * Copyright (C) 1999, 2000 Eazel, Inc. * * License agreement goes here. * * Author: John Sullivan <sullivan@eazel.com> */ #include <config.h> #include "nautilus-icon-factory.h" #include <string.h> #include <stdio.h> Include statements for other header files go here. Other declarations and code go here.