Catalase Website IndexJamesTechno •Coding Standards •

C++ Coding Standards


Experience of avoidable problems that caused additional work led me to rules for my own software development. These are in four sections:

Finally there are some links to other sites which contain coding standards and related information


TopDown to Changes

Approach

Things to think about when designing an algorithm.

Speed Now: Optimisations are NOT deferred to late in the project provided making them can speed up testing / development.

It's generally accepted wisdom that one should make it 'work right' before one makes it 'work fast'. This can mistakenly be taken to the length of postponing optimisation till the end of a project.

I have found slow versions of my software slowing testing and development as badly as a very slow compiler would.

Output Caches: When designing an in-class cache to speed up a function, it should be an OUTPUT cache. This is to ensure caching strategies are compatible between classes (i.e classes can be strung together).

Prior to this rule, I'd written pairs of classes where one class had an output cache, and it fed in to an input cache of another - a wombat (trans. Waste Of Money Brains And Time).

Think Extensions: Code for 'squares' should be written with extension to both cubes (and higher) and extension to triangles, pentagons (and higher) in mind.

This is best achieved by recognising that points, edges and faces have different types. For example corners of a polygon are fenceposts to the edges, and therefore an explicit function to move from edge to one or other corner is needed. By contrast, use of an edge index as if it were the same thing as a point index does not generalise to polyhedra.

Think Tabular: Look for ways to make the code more regular. Switch statements tend to accumulate clutter. Aim to make them regular in their structure with future migration to table driven code in mind

Fencepost Objects: Coordinate conversions and fencepost to fence conversions should be designed items, not ad-hoc conversions as and where needed. If efficiency is an issue here, use inline functions, but make the conversion explicit.

An example, a text 'cursor' is a fencepost object for it appears between characters. It's position therefore is of a different type to that of a character, and a function call to get the index of the character after it or of the charcter before it should be made.


TopDown to Specifics

Changes

Changes in code can be like rust. They can degrade a good clean structure. The 'changes' rules are aimed at controlling this effect.

Disable Obsoletes: When moving variables between classes or renaming, always delete or comment out the old variable declaration to ensure ALL cases of its use have been caught.
Dead Code: It is O.K. to leave dead code commented out in the source. Normal development will involve commenting out dead code. Such code may finally be removed much later, when it is clear it can no longer be useful.
Duplicates: It's O.K. to have two copies of similar algorithms, where a variant is being debugged. However, a long term goal should be to merge back these subroutines where the performance hit in 'worst case' is less than 10%.


TopDown to Names

Specifics

Simple rules that are closer in spirit to normal coding standards.

Outparams First: In functions, arguments used to return results should precede arguments which give data, e.g FuncCopy( pDest, pSource); This is the same order as in memcpy and assignment.

A refinement of this rule applies to classes. Function return values (where non void), count as the first argument. The implicit class variable (*this) counts as the next argument. A function which reads one class variable and writes another would be a member function of the class which it updates. The function would have the class it reads as an argument.

This rule thus determines which of two classes a function of two class variables belongs in.

While Iterators: Iterators should be for use in 'while' loops rather than in 'for' loops. Define a First() and a More() function - no need for a Next() function.

The function First() should take an argument specifying the kind of iteration as this reduces the number of different iterator classes one defines.

Use Spaces: The editor should be set to use spaces not tabs for indenting. One indent is three spaces, except for code auto-generated by MSVC wizards.

I have been caught out by printers that define tab sizes differently.

Named Constants: Constants should be given names where this improves clarity. This is particularly important for lengths, pixel positions and scale factors.
Avoid Bools: For readability of calls to a function and to promote future upgrades, avoid BOOL arguments to functions. Use, for example, an option code.
Undo Reverses: Aim to 'undo' operations in the reverse order to which they were done, increment then store becomes retrieve then decrement. Constructor / Destructor pairs may be useful to achieve this.
Separate Declares: Don't use the facility which allows a variable's declaration to be combined with a calculated assignment to that variable.

Because I use a variant of Hungarian notation (I use the p and sz prefixes), I find that I rarely need to look at declaration of variable types. I do need to look at assignments. Having declarations and assignments in separate chunks helps me to find what I need to faster. All the local variable type declarations are at the of a subroutine close to the subroutine's paramater variable type declarations.

Range [,) In comments describing ranges use the [0,3.7) convention where '[' indicates inclusive, ')' indicates exclusive.
Range Spec Ranges should be represented as start and nItems rather than as start and end.


TopDown to Links

Names

Rules for naming. Checking of these rules could be largely automatic. These rules are particularly important as searching for the correct name to use is one of the most frequent avoidable activities in programming.

Related Names: Look to create groups of related names rather than once-offs, and prefer naming where the repeated part comes first (which is useful for tracking related quantities in alphabetic listings). Exception: When providing functions like system provided ones, use names following system conventions, e.g CircleBlt(), ShrinkBlt().
American Spelling: Colour is spelt with a 'u' Exception: system provided identifiers/macros such as COLORREF use the American spelling. Normalize with a 'z' not an 's'.
Capitalisation: For purposes of capitalisation, these words are treated: As single words: These as two:
Bitmap, Background,
PreComputed, ScaleFactor
Informative Names: Avoid redundancy in names, e.g DibHandle, not MyDibHandle.
Be Specific: Be specific e.g. 'Points' or 'Positions' is preferred to more generic terms 'Values', 'Data'.
Positive Naming: Use positive naming, e.g. IsVisible() or IsHidden(), not IsInvisible().
Abbreviations: Abbreviations should only be used where the number of characters saved is significant taken over the whole program, i.e. abbreviate long names used many times.

Capitalise only the first letter of All-Caps abbreviations. This is a uniform way to distinguish lexical units in compound names (e.g. use Dib not DIB).

Standard abbreviations should be added to this list here: Com FuncArg Dib Rgb
Command
Function
Argument
Device Independent Bitmap
Red Green Blue

Standard Pairs: When naming 'inverses' use standard pairs. Add to this list here: LoadGet Open
& Save
& Put
& Close
Conversion names: Name conversion functions using Type1OfType2() for functions which act as a prefix, Type1ToType2() for class functions which act as a suffix. This is so that conversions can be read as:
  • Type1OfType2( Type2OfType3( argument )) or
  • Variable.Type3ToType2().Type2ToType1()
Coding Standards © James Crook, April 1998.


Top

Links:




Catalase Website IndexJamesTechno •Coding Standards •
"C++ Coding Standards" page last updated 5-July-2003