Coding conventions recommended by the Ceylan project

Overview

We listed here the main conventions we adopted regarding developments, in the hope of promoting:

  1. coding good practices [see our dedicated section]
  2. a well-documented, homogeneous and easily read code

This document currently focuses on general conventions and on C++ specific ones. It will nonetheless include hints for other languages as well, notably the sh shell, Python and Erlang.

In Makefile.am, header files (*.h) and source files (*.cc) should be listed alphabetically (hint: use /bin/ls -1 *.cc for example), each file being on a line of its own thanks to the \ separator, which should be one space after the variable declaration (ex: LOGS_INTERFACES = \), whereas all filenames, except the last, have a final \ at column 50, with no trailing space.

All tests should be named test + library name + subject, for example: testCeylanLogSource

Regularly, autoupdate should be used to have state-of-the-art configure settings:

autoupdate configure-template.ac diff configure-template.ac configure-template.ac~
Beware though, autoupdate changes must be carefully checked, the tool tends to remove useful [] quotes for example.

Not all Autoconf/Automake commands can accept variable substitution (i.e. if A=1 they would take litteraly argument $A as $A instead of 1). It is the case of at least: - AC_INIT - AC_CONFIG_SRCDIR - AC_CONFIG_HEADERS This lack of feature is rather annoying, since it prevents to define a value one time for all, in only one place. For example, we would have liked to be able to write: CEYLAN_MAJOR_VERSION=0 CEYLAN_MINOR_VERSION=3 CEYLAN_RELEASE=0 CEYLAN_VERSION=$CEYLAN_MAJOR_VERSION.$CEYLAN_MINOR_VERSION CEYLAN_FULL_VERSION=$CEYLAN_VERSION.$CEYLAN_RELEASE CEYLAN_MAILING_LIST_SUPPORT="ceylan-support@lists.sourceforge.net" CEYLAN_CONFIG_HEADER="code/CeylanConfig.h" AC_INIT([Ceylan], $CEYLAN_FULL_VERSION, $CEYLAN_MAILING_LIST_SUPPORT) AC_CONFIG_SRCDIR($CEYLAN_CONFIG_HEADER.in) AC_CONFIG_HEADERS($CEYLAN_CONFIG_HEADER) etc. Instead of which, we had to resort to the following work-around to secure the version one-time assignment: AC_INIT([Ceylan], m4_normalize(m4_include([conf/build/version.inc])), [ceylan-support@lists.sourceforge.net]) with version.inc being: dnl="This rather convoluted file allows to centralize version numbers while " dnl="being able to be both sourced by shell scripts and included by m4." dnl="It can be generated by the 'generateVersionFile.sh' script." dnl=; MAJOR=0; MINOR=0; RELEASE=3 ; RELEASE_DATE="Saturday, January 28, 2006"; m4_hiding_string="\ 0.0.3 dnl " As this is a rather complicated scheme and it solves only the version issue, we finally preferred to come back to our first move: generating 'configure.ac' from a template ('configure-template.ac') and settings (read from 'CeylanSettings.inc'). In the template, Substitution Targets (tags beginning with 'ST_') are replaced by their values as defined in the settings file. This cumbersome process should only apply to variables whose definitions would have to be duplicated because of configure.ac calls. Release:

Before you release a distribution of your project, it is wise to get the latest versions of `config.guess' and `config.sub' from the GNU site(21), since they may be newer than the versions automatically added by libtoolize and automake. Note that automake --add-missing will give you its own version of these two files if `AC_PROG_LIBTOOL' is not used in the project `configure.in', but will give you the versions shipped with libtool if that macro is present! next: when API is stable and there are users, use libtool library versioning http://sources.redhat.com/autobook/autobook/autobook_91.html#SEC91

Make sure that #if is used instead of #ifdef, since setting a symbol to a value or another (typically, -DA_CEYLAN_SYMBOL=1) is more robust than setting a symbol or not (typically, -DA_CEYLAN_SYMBOL). For example, the -Wundef gcc options issues a warning if an undefined identifier is evaluated in an `#if' directive.

The problem arises when using AC_CHECK_HEADERS: either, if the header is found, it defines and set to 1 a variable (ex: #define HAVE_DUP2 1), either, if the header is not found, it does not define anything special (ex: it does do #define HAVE_DUP2 0 for instance).

So, to rely on -Wundef for our own defines, we may test them with #if CEYLAN_DEBUG and enforce setting a value for each of our symbols (they are defined in all cases, ex: #define CEYLAN_DEBUG 0 or #define CEYLAN_DEBUG 1), and test AC_CHECK_HEADERS-ones simply with #ifdef HAVE_DUP2 instead of #if HAVE_DUP2 (otherwise -Wundef would issue warnings or errors about it).

#define HAVE_DUP2 1

General purpose coding conventions

File layout

Source file are formatted so that they are best viewed with an 80-column wide text editor, with tabulation stops being four space wide.

Nommage des fichiers

Préfixer par nom de projet (ex: Utils.h et Log.h) namespace commencer les noms de membres statiques (méthodes et données) par une majuscule.

Certains utilitaires d'archivage, tels qu'au moins certaines versions de WinZip, ne supportent pas que deux fichiers, situés à deux endroits de l'arborescence, portent le même nom (tous les fichiers sont stockés ensemble dans l'archive, indépendamment des répertoires). De ce fait, des conventions voudraient que, les répertoires ne formant pas toujours des espaces de nommage cloisonnés, les noms de fichiers soient par exemple préfixés par le nom du projet (ex: OSDLVideo.h). On essaie de respecter cette habitude, dans la mesure du possible.

Bonnes pratiques C++

Fichiers

Correspondance entre type de fichier et extension:

Type de fichier Extension
Fichier header (interface) .h
Fichier source (implémentation) .cc

Nommage

Expressions

iff means if and only if. Used when two logic expressions are equivalent.

General C++

Of course, a preferably virtual destructor should be defined on these cases, and at least a classic (non-copy) constructor. Never throw an exception from a destructor, doing so is calling for a disaster.

For all classic constructors that can be called with only one argument (be there only one argument, or multiple arguments, with at least all but one having a default value), the explicit keyword should be used. For example, a declaration such as explicit Object( bool trackInstance = true, bool dropIdentifierOnExit = true ) throw( Log::LogException ) should have the explicit keyword, since a clumsy attempt to call a function f( Object a ) with f( this ) ; (which is an error indeed since it should have been * this, this being a pointer) apparently is interpreted by the compiler as: f( Object( static_cast<bool>( this ))): the pointer this, a numerical type, is converted to a boolean so that the copy constructor can be called as if its two arguments were true (trackInstance because of pointer, dropIdentifierOnExit because of default value).

Method order

The three main sections should be not split, and should be ordered this way:

  1. public
  2. protected
  3. private

When enumerating the methods of a class, be it for declaration (*.h) or definition (*.cc), the same order should apply, in each of the three previously discussed main sections:

  1. constructors
  2. virtual destructor
  3. non inherited non-static methods
  4. inherited methods
  5. static methods

For attributes with get/set accessors (ex: getPeriod and setPeriod), the get method should appear before the set one. If a has method is available (ex: hasPeriod), it should figure previously (has/get/set order).

Please react!

If you have information more detailed or more recent than those presented in this document, if you noticed errors, neglects or points insufficiently discussed, or if you would like to contribute and help us, even a little bit, drop us a line!




[Top]

Last update: Monday, January 3, 2011