Skip to content
This repository has been archived by the owner on Jul 16, 2022. It is now read-only.

General Programming Guidelines

jchoude edited this page Apr 16, 2013 · 1 revision

Introduction

These are generic programming guidelines that will ease the development of more robust and easily maintable code. They will also ease catching bugs.

Code without warnings

Compilers generated warnings can be annoying, but they should not be ignored. Even if they sometimes look useless / oversensitive, they can (and often do) signal a potentially dangerous situation. For example, if a warning is issued concerning a comparison between a signed and unsigned integer (in a for loop, for example), there will rarely be a real problem. However, if a developer makes an error in the increment or stop condition of his loop, the signed / unsigned error can have bad consequences, like trying to read in an non-existing part of a container, which would likely cause a runtime crash.

Therefore, at all times, try to make sure that your code does not generate warnings.

In some special cases (for example, because of differences between compilers on different platforms), some warnings are tolerable, because correcting them would require uselessly contrived code. However, before concluding that this is the case of your current warning, be sure to thoroughly understand the issue, and document the reason of the problem.

Be const correct

C++ provides the const key word to allow passing as parameters objects that cannot change to indicate when a method or function doesn’t modify its object. Using const in all the right places is called “const correctness”.

It’s hard at first, but using const really tightens up your coding style. Const correctness grows on you.

Note that const can always be casted away but that this practice is highly discouraged.

Do not prematurely optimize

This is one of the most important tip for C++ development: do not try to optimize local, detailed code for the compiler. The compiler will usually do a better job optimizing local code, while the programmer will spend better time optimizing the general execution flow or algorithmic complexity of his code.

Use NULL to assign to null pointers

The title says it all. Use NULL when assign the null pointer to a pointer. The syntax is clearer, it is easier to search for, and with the forecoming null_ptr type of the C++11 standard, il will be easier to replace or implement.

int* pCount( NULL ); // Is more explicit than 
int* pCount( 0 );

Put only one statement / variable per line

There should be only one statement per line, unless the statements are very closely related. This makes the code easier to read. In the same line, never define multiple variables per line. This will allow you to place documentation on every line. It will also clearly show that variables are initialized or defined, and will reduce the probability of declaring a variable when you actually meant to declare a pointer.

Don't:

char **pA, *pX;

Do:

char** pA = 0;  // add doc
char*  pX = 0;  // add doc

Use the initialization list

When possible (and it should almost always be possible), use the initialization list of the constructor to initialize the data members of a class.

Furthermore, members in the initialization list should be in the same order as they are declared in the class, since they are initialized in the order of declaration. This removes some possible behavior problems, and also removes warnings issued by compilers on OSX and Linux.

If the class is declared like this

class A
{
  int m_count;
  int m_length;
  char m_id;
}

The initialization list should look like this

A::A
  : m_count(0),
    m_length(0),
    m_id('a')
{}

Do NOT use goto, continue and break, except if absolutely needed

Goto

Goto statements should be used extremely sparingly, as in any well-structured code. The main and possibly one of the only place where they can be usefully employed is to break out several levels of switch, for and while nesting. That being said, the need to do such a thing may indicate that the inner constructs should be broken out into a separate function, with a success/failure return code.

Continue and Break

Continue and break are really disguised gotos so they are covered here.

Continue and break, like goto, should be used sparingly as they are magic in code. With a simple spell the reader is beamed to god knows where for some undocumented reason.

Break is usually accepted and can be well used if the reason for the break is clear to the reader and well documented, by simple surrounding code or by comments in the code itself.

Continues are a little bit more problematic as they may bypass test conditions within a loop or bypass increment/decrement expressions if they are contained within the loop. (This applies mostly for while loops, as for loops have their increment/decrement expressions into the loop definition.)

That said, a further rule may be given: Mixing continue and break in the same loop is a sure way to disaster.

Do not default an IF Test to Non-Zero

int* pVar;
if( pVar != NULL ) 

is better than

int* pVar;
if( pVar )

An explicit test will be more obvious to the reader as it will clearly show the numeric (not Boolean) nature of the test. At the same time, if the tested variable or pointer is ever replaced by an object which supports an implicit cast to Boolean, the comparison will return an error or a warning while a default test will not.

Use the correct data type for the job

Don’t use floating-point variables where discrete values are needed, even for the sake of generalization. Using a float for a loop counter is unpredictable due to round-off errors. Always test floating-point numbers using error threshold rather than direct “==” or “!=”.

Use blank lines for structure

Try to add blank lines between sections of code when sections are unrelated or express a different stage of the function execution.

Avoid the accidental omission of a "="

Accidental omission of the second “=” of the logical compare is a problem. The following is confusing and prone to error :

if( abool = bbool ) { ... }

Does the programmer really mean assignment here? Often yes, but sometimes no. The solution is to just not do it. Instead, use explicit tests and avoid assignment with an implicit test. The recommended form is to do the assignment before doing the test:

abool = bbool;
if( abool ) { ... }

In the same context, refrain from mixing assignments and comparisons within a condition.

if( ( a = b ) > c ) { ... }