We listed here the main conventions we adopted regarding developments, in the hope of promoting:
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~
|
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).
Source file are formatted so that they are best viewed with an 80-column wide text editor, with tabulation stops being four space wide.
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.
Correspondance entre type de fichier et extension:
Type de fichier | Extension |
---|---|
Fichier header (interface) | .h |
Fichier source (implémentation) | .cc |
namespace
myNamespace { ... }
, utilisation: using namespace
MyNamespace
ou MyNamespace::MySymbole
).
iff
means if and only if. Used when two logic expressions are equivalent.
MyClass( const Myclass & source ) throw()
MyClass & operator = ( const MyClass & source ) throw()
)
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).
throw()
would be used).using std::string
or, even worse, using namespace <a namespace>
, since every code which would include this header would have global names declared, possibily hiding a lack of internal declaration. Consider, as much as possible, using explicit prefixs each time a type name defined in a namespace is used. Therefore, prefer in header files std::string myString
to using std::string; [...] string myString
.myType
, aFunction( myType & arg)
and myType * aVariable
), the compiler does only need a declaration for it (such as class myType ;
). It is not necessary for it to have this type's definition. Therefore, only declaring this type in the header file (without #include ...
) would accelerate compilation, since less files should have to be read and parsed. For big projects, the gain is obvious.struct
data type, and prefer using class
declarations: a struct
is indeed a particular case of a class
, thus a full use of class
data type with corresponding public
, private
and protected
statements should be more uniform, and clearer for the reader.0
(zero) than to NULL
or null
or anything else.true
and false
boolean values should be used directly, not as #defined' TRUE
or FALSE
X.c
should begin with #include "X.h"
, then #include "Y.h"
, then #include <climits>
, for example. This allows to check immediatly that the corresponding header file (here, X.h
), is defined with all its dependencies, and therefore does not rely on any external includes.Ceylan::Log
one, with its log plugs.MyObject::MyStaticMethod
, not MyObject::myStaticMethod
void * myPointer; if ( myPointer ) ...
less readable than if ( myPointer == 0 ) ...
The three main sections should be not split, and should be ordered this way:
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:
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).
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!