You are here: Foswiki>WorldFoundry Web>CodingStandards (29 May 2003, KevinSeghetti;)Edit Attach

World Foundry Coding Standards

These are the latest coding standards, not all of WorldFoundry adheres to this, mostly due to changes in the standard over time, but it is what all new code should follow. This has been modifed from the OldCodingStandards by KevinSeghetti,there are probably still errors and inconsistencies in it, and he still needs to add much more to it, feel free to contact him with suggestions or to discuss any of these standards.

File extentions:
  • .hp : C++ header file
  • .hpi : C++ inlines (included by the .hp file)
  • .cc : C++ code
  • .h : C header file
  • .c : C code

Never use multiple inheritance

When WorldFoundry was started MI was too broken, so there isn't any there now, and the code base doesn't seem to suffer for it, if you feel the need to use MI contact KevinSeghetti and we can talk about it. Update: There still isn't any MI in WF, but I am rapidly getting to a point where I might use it (to merge Render and Physical interfaces).

Never use exception handling. Flawed in implementation

WorldFoundry doesn't have any now, but this one might change in the future.

Never put inline definitions in the class declaration.

All inlines should go in the .hpi files.

Never overload the following operators:

* (unary) & (unary) = = , ->* -> () ! &&

Never use a cast where a constructor conversion is occuring.

Use the new style of conversion. e.g., int( foo ) rather than (int)foo

Never pass NULL objects by reference.

For gcc this means that we should be able to compile with the -fnonnull-objects flag and preserve the correctness of the program.

Avoid classes consisting of purely static members (modules).

These may be equivalently rewritten as normal members with a single static instance of the class, thus unifying class styles with more normal classes.

Avoid operator overloading unless it really makes sense, and makes the code more clear.

The only case this should be used for is types which are inherently numeric. Common examples are Scalar, Vector, and Matrix classes, which override all the integral operators as one would expect for linear algebra.

Avoid using pointers to functions or pointers to members.

There is probably a better way to do it.

Avoid explicit casts.

For example, when refining subclasses, use a virtual translation function instead (so that errors can be detected). Production versions may conditionally compile to casts where speed is important. what does this mean? can you give an example? -- WillNorris At this stage I think conditionally compiling to casts is not a good idea, but the idea was to have something like: dynamic_cast<foo*>(bar) in the debug version, which becomes reinterpret_cast<foo*>(bar) in the release version (thus giving up the type checking, which does take a few cycles). -- KevinSeghetti

Avoid passing/returning classes.

Always comment the use of constructs mentioned above.

Use the keyword UNAVOIDABLE in a comment near such code.

Always use an explicit :: to mark the scoping of C++ external functions.

Always use meaningful names for identifiers, especially any identifier which has global scope.

Always use const for input only parameters.

Also be sure to always label const functions as such. (The simplest way is to assume a const function until proven otherwise.

Usually pass objects by reference rather than pointers.

Unless one explicitly needs the concept of an invalid object (i.e. a NULL pointer), or one explicitly needs the concept of a pointer (e.g. for memory allocation).

Usually use constructors and destructors where it makes sense.

Thus initialization and cleanup are always in a known place. Do not be scared to use dynamic memory allocation, especially for initialization. Likewise, a useful idiom is to create a new block (which makes the object on the stack) and construct and object (rather than an object pointer) which is automatically destroyed when the code exits the block.

Conventions

We assume that all .cc files have a corresponding .hp .hpi (which may be empty). All externally visible class definitioins go in the .hp file, all inline definitions for such classes go in the .hpi file.The .hp file additionally contains any globally visible #defines, enums and/or typedefs associated with the classe(es) defined. There will be no restriction about how many classes are defined in a .hp file (and of course corresponding .cc files).In the special case of a single class, however, the file name should strongly reflect the class name. In other cases the classes should be tightly releatd, such that inclusion into one interface makes sense. The .hp header includes the .hpi file and the end, like this:
#include "abstract.hpi"        // include inline definitions

All headers are to be included only once, with the #ifndef/#define as the first thing in the file, followed by the banner, e.g.
#ifndef _ABSTRACT_HP
#define _ABSTRACT_HP
// banner
...
#endif    //!_ABSTRACT_HP
We make an explicit mapping from filename to internal identifier as follows (given as a perl script):
tr/A-Za-z/a-zA-Z/;    # invert case for letters
s/[^A-Za-z0-9/_/g;    # anything weird (e.g. '.') becomes a _
s/^/_/;               # prepend an underscore
An alternative would be to simply go to uppercase, but this convention is attractive because it does not lose any information if filenames are restricted to letters, numbers, and '.'.

Note that the .cc file MUST include the corresponding .hp file

Naming conventions

Class names should follow the SmallTalk class naming convention of having the first letter of words being capitalized, e.g. ClonableObject. Enumeration type names should similarly treated, except for beginning with an 'E', e.g. EZoomState.

!#defined constants, const values, or enum constants should be all uppercase to indicate their status. Enumeration constants have the additional constraint of beginning wit EXXX_, where XXX is an optional enumeration type indicator, e.g. enum EZoomState (EZS_NONE, EZS_ZOOM_IN, etc.)

typedefs of primitive types (which should be the only kind used, typically), should be lowercase, with a trailing "_t". This convention is rooted in ancient UNIX/C tradiion. This is also not a strict convention, for example the primitive types uint16, etc, do not follow this because their meaning is clear from the name. For less obvious types, however, this convention quickly alerts the reader to the meaning of the symbol.

Class data members must always begin with an '_', which immediatly distinguishes them from local variables or function parameters.

Variable names should begin with a lower case letter, but otherwise capitalize the beginnings of words.

Functions which take boolean arguments should define, as part of the interface, symbolic names which indicate what they do, e.g.:
#define bGenError bTrue
#define bNoGenError bFalse
...
getCelRefFromDB(const char* name, Bool fGenError);
This makes the use of such functions much more readable. Notice the implicit naming convention for such boolean constants. I happen to like this convention even though it violates the all upper case convention for constants.
Access functions should typically be the same name as the data they represent for the fetch (with no leading '_'), similarly for the set function, except with a leading "set", e.g.:
public:
    sometype    NameOfData(void) const;
    void        SetNameOfData(sometype);
private:
    sometype    _nameOfData;

Base types and constants

In addition to basic types and constants defined in ANSI, such as size_t and NULL, several common types are defined to help facilitate portability. These include the following integral sized types and abbreviated notations:
int8    uint8    int16    unint16    int32    uint32
uchar   ushort   uint     ulong

In addition to the integral types a boolean type is defined: Bool. This is almost guaranteed to be type int, since one prefers the natural machine size. (In particular it will not in general be of type char.) The following are predefined boolean constants:
#define TRUE    (1!=0)
#define FALSE   (1==0)
#define bTrue   TRUE
#define bFalse  FALSE

Class organization

This defines the order in which things are defined for a class. There are four main groups:
  1. All public members except for (non-static) data members.
  2. All protected members except for (non-static) data members.
  3. All private members except for (non-static) data members.
  4. All data members in the following order: public (should not have any of these), protected, and private.

Inside of the first three groups, the order of definitions is as follows:
  1. virtual methods which override methods of a base class. (One could include comments between blocks of functions which have the name of the base class which first defines the method.) The access should stay the same for derived classes, i.e. once a virtual member is declared protected it should not be later depracated to private.
  2. Virtual methods which are first defined in this class (We presume that such methods will get overridden by derived classes). Again, explicitly include access here.
  3. static member functions
  4. member functions

Other Conventions

Tabs are encouraged, and should be the equivalent of 4 spaces.

Non-Conventions

These are not explicitly spelled out since doing so would cause more trouble than remaning silent. Commenting style Brace, parenthesis, stuctured style Line lengths (withing reason, of course...)

The guiding principles are clarity and self-consistency. It should be easy (and not too painful) for others to mimic the style. The primary developer of a module gets to set the style for that module.

Debugging

Any program or sub-program (module) must have as a minimum two distinct parts: the production version and the debuging version. Typically there is only one prodcution version, while there may be many levels of the debugging version. This is implemented using conditional compilation. The following describes an ideal debugging module. It should be impossible to use it incorrectly. Literally. Any sequence of calls coupled with any value for parameters should either succeed (which should guarantee correct usage), succeed with warnings, or fail. Thus at a mimimum, all input parameters must be checked for acceptable values, as well as all states, which can be checked given a particular function. A new developer (or more likely the original developer many months later), should be able to add functionality to the module without any concern that it may silently violate previous assumptions.Thus all private and protected functions must be exactly as robust as the external interface.
While C/C++ is not the ideal language to accomplish this goal, it is often easier to do that one may suppose. The crucial asset which can be used for this task is the assert. If, while writing some code, every assumption is explicitly stated using as assert, it becomes very difficult to voilate the integrity of the module. Comments about assumptions should be considered obsolete, except as commentary on an assert. The two primary obstacles to the success of achieving the goal of an ideal module are fear of inefficiency and laziness. Both must be fought constantly. If we remember that 90% of execution time is spent in 10% of the code, then it makes very little difference how much extra checking we do in the other 90% of the code. And even in that critical 10%, the only excuse for not checking state is if it actually does run unbearably slowly. In this case one can conditionaly compile more robust checking when tracking difficult bugs. One must always remember that this is not the production version. As for laziness, this is the difficult one. The best trick is for the use of assert's to be so ingrained that it becomes automatic.

Important Note: asserts must never contain side-effects, since they are compiled out of the release version.
Topic revision: r1 - 29 May 2003, KevinSeghetti;
This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback