Recent Changes - Search:

Documents

Download

Building

Support

Wiki Stuff

edit SideBar

CodingStandards

Introduction

Coding standards can be an issue that can inspire a certain amount of passion amongst programmers. The most important thing is not what the coding standards are but that they exist, and followed. This makes the code easier to read and debug, and helps to avoid some of the more easily made mistakes that can occur when programming in C++.

PWLib and OPAL/OpenH323 have been written using a consistent coding style that has been in use continuously over the last 10+ years. It may not be what you are used to, but please follow it anyway! If you are a professional programmer you have undoubtedly needed to adapt to your employers standards I number of times, do so again.

Filenames

Source files have the suffix .cxx and header files have the suffix .h. It's just the way the we have always done it.

Remove all compiler warnings

Most modern compilers report warnings at compile time for a good reason - they usually point out real or potential problems in the code. Sometimes, an important warning can hide inside a list of benign warnings - so all warnings should be removed to avoid developers becoming "desensitised".

Don't use globals unless absolutely necessary

There should be very few places where you need a global. Gnerally what you consider a "global" can just be added to your derived version of the PProcess class. True globals or non-const statics outside of scope of a function are bad. This is especially true of C++ composite classes. There are many portability issues with how and when a global variable is constructed and destroyed. So, the best thing is to "just say no".

However if you absolutely must have a global, use a singleton, i.e. wrap it in a function, e.g.

class MyClass
{
  ....

  static MyClass & Instance()
  {
    static MyClass instance;
    return instance;
  };

  ....
};

And this resolves most of the construct/destroy issues. Though you may still get memory leaks reported even though there really isn't any.

Initialise member variables in the constructor using the initialisation list

Where possible, initialise member variables as part of the constructor initialisation. Also note that some compilers (gcc) will complain if member variables are not initialised in the same order they are declared.

MyClass::MyClass(int, a, int b, int c)
  : BaseClass(a)
  , m_B(b)
  , m_C(c)
{
}

A minor thing, you can put the commas at the end of the line as is more traditional and we wont get upset, but the above method has the advantage of it is really easy to move them if the order is wrong, and really easy to add conditional compiles (#if) without needing to do strange things with commas.

Always use reference parameters to functions

A C++ reference is just a pointer with attitude, so always pass function parameters by reference rather than value, like so:

Correct


void MyFunction(const PString & str)
{
  cout << "The string is " << str << endl;
}

Not correct


void MyFunction(PString str)
{
  cout << "The string is " << str << endl;
}

Passing by const reference will have exactly the same effect as passing by value, but without associated memory and speed overhead of copying the object, and should be used in all cases unless the function needs to change the value of the argument.

Hopefully it should be obvious that there is no point in passing something like "const int &", just pass the int.

Try to read statements as a sentence

There is a trend to reverse conditions in if statements. For example:

    if (NULL == personalData)
      return;

The rationale is that you can't accidentally put an assignment into the conditional (personalData = NULL rather than personalData == NULL) however most compilers will warn you if you do this now, so it is not very important.

    if (0 > age)
      age = -age;

However, the second statement does not even have that excuse. Apologies for the non-English speakers, but you simply would not say "Zero is greater than my integer", you would say "my integer is less than zero". This kind of code just makes it harder to understand as you cannot read the line like an English sentence.

Talk to your technical writers if you want to here all about how to write English, engineers should never be allowed to do it!

    if (!publicData)
      return;

This also applies to using ! substituting for == NULL. The English sentence is "if not pointer" which does not make sense as not is a boolean operator. C== is quite lax about applying operators to all types, so the programmer should make it more obvious. Is the publicData variable an integer? A pointer, or a boolean? How do you tell?

    if (isOld == TRUE)
      return;

The final example is tautological, and can be dangerous just in case TRUE is not "1".

So, in combination with good variable naming practices, you can make the line read like a sentence. This will help following the code immensely.

Declare locals near use

C++ allows variables to be declared anywhere in a block of code. This comes as a great shock to C programmers who are used to declaring everything at the top of a code block. This can greatly increase the readability of code and can avoid having to search backwards several pages to find a variable declaration.

Identifier names

Identifiers are composed by concatenating words with leading capitals on each word, we don't use the "_ as space" method. Use of highly abbreviated identifiers is discouraged - it's not like you have to pay for each extra character used.

Types, classes and functions all have a leading upper case character (e.g. TestClassName), whereas variables have a leading lower case character (e.g. thisIsAVariable). There is a growing tendency to prefix class member variables with "m_", and this is acceptable, but full Hungarian notation (m_szVariable) is not required.

All global scope classes and types exported from PWLib have a "P" prefix,and from OPAL have "OPAL", "SIP" or "H323" prefixes to avoid name space collisions with other libraries and applications.

The code generated by the ASN parser prefixes all member fields with "m_" and all emumeration values with "e_".

Indenting & Spacing

This is the most contentious of all of the coding standard rules. As it gets to aesthetics and there are so many variations! Here are the most important rules.

All if, for, while and switch statements are indented two spaces per level. And you always indent, never put the statement at the end.

Most importantly - never use tabs. Always use spaces. Not all of us have or wish to set our "tabstops" to 2.

Open braces go on the trailing edge of lines unless they are the opening brace of a class definition of function. No open brace is required if the control block is a single statement, however it must be on the next line and indented.

Correct


class Class
{
  public:
    int Function(int arg);
}; 

int Class::Function(int arg)
{
  if (arg < 0)
    return -1;

  if (arg > 0) {
    switch(arg) {
      case 1 :
        cout << arg;
        break;
      case 2 :
        cout << "bad";
        break;
      default:
        break;
    }
  }
} 

Not Correct


class Class {
  public:
    int Function (int arg);
}; 

int Class::Function (int arg) {
    if(arg<0) return -1;
    if(arg!=0) 
    {
        switch(arg) 
        {
        case 1:cout<<arg;break;
        case 2:cout<<"bad";break;
        default:break;
        }
    }
}

Really wrong


class Class { public: int function(int); };

int Class::function(int Arg){
  if (Arg < 0) return -1;
   if (Arg != 0) {
   switch(Arg) {
   case 1:cout << Arg;
   break;
   case 2 :
   cout << "bad";
 }
}

Of lesser importance, but stuff that it would be nice for you to follow:

Be generous with spaces, you are not paying for them. e.g. After if or while, after a case value, between operators in expressions.

However, do not put a space between the function name and it's opening parenthesis. This is illegal for macros in some compilers, so don't get used to doing it and have to remember the exceptions.

Try to avoid macros. Use an inline function instead, this is C++ not C!

Put two blank lines between function implementations. There will be lots of single blank lines within the function, so a double blank line makes it even clearer than the brace at the left where the function ends.

And put single blank lines in your code. Between little logical groups of statements that perform a particular subtask. It should be headed by comment too, but we won't go that far!

Edit - History - Print - Recent Changes - Search
Page last modified on December 04, 2007, at 02:19 AM