C++ programming style guide, with comments on Windows API

by Andrew M. Steane, partly following the book "Computing Concepts with C++ Essentials, Third Edition" by Cay S. Horstmann, published by John Wiley & Sons, copyright © 2003, by John Wiley & Sons. All Rights Reserved.

Contents

  1. Programming style
  2. Windows API notes
  3. A few notes on microsoft visual c
  4. Other links:
    C++ reference at cplusplus.com
    C++ reference at cppreference.com
    Matt Mahoney's "How to Program in C++", mirrored here

These are some notes on my programming style, using C++ and Windows API (application programming interface). All Windows programs except console programs must interact with the Windows API regardless of the language.

For excellent tutorial help as well as detailed reference, I recommend Matt Mahoney's site at cs.fit.edu:
C++ by Example by Matt Mahoney: tutorial, examples, and reference.

Introduction

The problem with C++ is that there are too many decisions to make: there are too many ways to do any given thing. For example:

  1. When passing parameters which can be modified, shall we use pointers or pass by reference?
  2. Where shall we put lines of code: in a separate function? in a separate file?
  3. In a for loop, shall we put instructions in the for (...) statement or in the {...} code that follows it?
  4. For strings should we use char array or the standard library string class?
  5. For arrays of numbers, shall we use a basic array, a vector or a valarray?
  6. Should we use lots of global functions, as in C, or keep almost everything in classes?

A style guideline is essential to take away the low-level decision making so that you can get on with thinking about the coding task in hand.

Windows

When you start to write a Windows program, it is disconcerting that the computer appears to be deaf and dumb! That is, there is no simple input and output function like cin or printf. It is hard to get started with Windows API because there are too many unfamiliar variable names and function names, with little help as to how to get started in a simple way. msdn provides an excellent service in many ways, with a wealth of information, but there is too much information! The user is thrown in at the deep end, with example code fragments that are incomplete and use unfamiliar types even when they are not needed. Also many of the the examples provided by msdn have (at the time of writing, 2009) truly awful programming style (e.g. huge switch statements, variables defined far from their use, little attempt to restrict the scope of declarations). The second section of these notes provides a few guidelines on writing clear and succinct Windows programs.

Follow this link for a few notes on the visual studio integrated developer environment and the microsoft visual c++ compiler.

C++ style

Here are some style guides, mostly following recommendations by Horstmann:

  1. Tabs are set every three or four spaces, and converted into spaces.
  2. Leave a blank space after a keyword, but not after a function name:
       if (x == 0) ...;
       f(a, b[i]); 
  3. Variable and function names are lowercase.
  4. Constant names are uppercase. Class names start with an uppercase letter.
  5. Put spaces in expressions to clarify what the expression means. e.g. x = a + b*c;.
  6. Braces must line up (either horizontally in a single line, or vertically across many).
  7. No magic numbers may be used.
  8. Every function must have a (thorough) comment.
  9. Define each variable just before it is used for the first time.
  10. Attach the * to the type not the variable name in declarations.
    e.g.  int *pj ; /* no */
          int* pj ; /* yes */ 
  11. Make free use of braces (even where not necessary) to improve readability.
  12. Use const not #define for constants.
        e.g.  #define CLOCK_RADIUS 5          /* no */
              const double CLOCK_RADIUS = 5;  /* yes */  
  13. No goto is allowed (with rare exceptions).
  14. Try to keep to a small limit, say 30 lines of code, per function or per case.
  15. Try to avoid global variables. If you do use them, limit to a few per file.
  16. Make parameters clear through their name or type,
        e.g.   Employee remove(int d, double s);                  /* Huh? */ 
               Employee remove(int department, double severance_pay); /* OK */ 
               Employee remove(Department d, Severance_pay s);        /* also OK */  
  17. When spreading a long statement over several lines, break it (if possible) so that each new line begins with something like an operator which can't be the state of a statement. e.g.
    a = ..........................
       + ........................; 

Horstmann also has some other recommendations, such as no continue or break, but I don't follow them.

Example of use of braces (9.) e.g. NOT

if (...) 
   if (...) ...; 
else               // bad layout! will not do what the indentation suggests
{  ...; ...;  }
instead use
if (...) 
{  if (...) ...; 
   else             
   {  ...; ...;  }
}  // this {} not strictly necessary but greatly improves readability

I have some further style choices of my own to add, some as recommended by Stroustrop:

  1. Prefer ++j to j++ (because it is slightly quicker!)
  2. Prefer while(){} to do{}while()
  3. Prefer static to global
  4. Avoid hard-to-read "cleverness" in for() statements.
  5. Don't insist on succinct code when it reduces readability for no significant improvement in speed.
    e.g.  if (a=b) ...     // Don't. Poor style because it looks like "if (a==b)"
          a=b; if (a) ...  // much clearer 
  6. Keep temporary variable definitions local by use of braces
    e.g.  {  int cw = con.tm.tmAveCharWidth;  
             int ch = con.tm.tmHeight;     
             TextOut (hdc, cw, ch / 2, "hello", 5) ;
             TextOut (hdc, cw, ch / 2, "world", 5) ;
          }  
  7. Use return as early as possible in functions. It is fine to have multiple return statements: prefer this to using break, for example.
  8. Do not use references except in passing parameters to functions (see next point). If you want to manipulate a variable indirectly, use pointers. The exception is when you use a reference as a shorthand, but then define it immediately before the relevant statements and don't use it far from its definition.
  9. Try to use const where appropriate. For example, you would normally pass a c-style string by a pointer, but if the string won't be modified, then say so. e.g. display_message(const char* s) The const here means that the pointer can be moved, but the thing it points to can't be changed, i.e. the logic is (const char)*.
  10. Pointers vs. pass by reference for function arguments. My policy is motivated by the idea that the code should read naturally. For arrays, use a pointer to the base of the array (i.e. standard C/C++ practice). For non-array parameters that may be modified, use pass by reference (like in Pascal), but make sure the function behaviour is obvious every time it is called. That is, if it is not already obvious that the function can modify the parameter, add a comment to make it obvious (see next point).
  11. Adopt a notation to signal to the (human) reader the presence of input and output arguments in function calls and declarations. This can be done by user-defined types such as "int_I", "int_O" but I like to use a comment notation /**/ as follows. If a function produces more than one output value you should consider whether the outputs could be placed in a structure. If they are best kept separate, then use the order
    val = func(input pars, /**/ output pars)
    where the equivalent Matlab syntax would be [val,output pars] = func(input pars). For example
    double range(double y[], int n, /**/ double& minval, double& maxval)
    {...}
    ...
    r = range(y,n, /**/ ymin, ymax);
    
    would put the range in r while also placing min and max values in ymin, ymax. The idea is that the /**/ symbol inside a list of parameters is used to signal which are input and which output: the parameters after this symbol can be modified by the function.

    An exception to this policy is for functions such as strcpy where the whole point of the function is to take something and put it somewhere else. Then the ordering func(output,input) is used.

    If some parameters provide both input and output, it is fine to send them twice provided that one is as a reference, the other as a value. For example

    char* extend(char* string, int n, /**/ int& newn);
    ...
    s = extend(s,n, /**/ n); 

  12. The order of material in a given source file is
    • Header comments
    • #include statements
    • Constants
    • Global variables
    • Classes
    • Functions
    Try to keep each source file of manageable length.
Classes

Lay out the items as follows:

class Class_name 
{ 
public: 
    constructors 
    mutators   
    accessors 
private: 
    data 
};
Supply a default constructor for every class.

Avoid the use of friend, except for classes that have no public member functions. Horstmann, Stroustrop and others advise that all data fields of classes must be private. This is in the right spirit but I don't always stick to it.

When overloading operators, make single-operand operators such as =, ++, += member functions (methods) of the class. These can return the result by reference. Make double-operand operators such as + non-member functions, which work by calling += and return the result by value. e.g.

class Widget
{
public:
   Widget();
   Widget(const Widget&);
   
   Widget& operator += (const Widget& rhs);
   
   int geta() { return a; }
   double getb() { return b; }
private:
   int a;
   double b;
};

Widget::Widget() { a=1.0; b=10.0; };  // default constructor

Widget::Widget(const Widget& w)   // constructor to make a copy
{ a=w.a; b=w.b; };  

Widget& Widget::operator += (const Widget& rhs)
{  a += rhs.a;  b += rhs.b;  return *this; }

Widget operator + (const Widget& rhs1, const Widget& rhs2)
{  Widget result(rhs1);       // make a copy
   result += rhs2;            // use the += method already defined
   return result;             // this returns the result by value
}

Overall philosophy

Try to regard a program not as a set of functions and data, but as a set of concepts. Each generic concept should appear as a class or a typedef. Each instance of the concept is an object. Think of an object as "data with teeth". You should regard the "teeth" (i.e. the methods) as powerful but light-weight, i.e. they do not cost memory. The program functions like a managing director rather than multi-skilled worker: instead of "plot the data" the philosophy should be "tell the data to plot itself".

Make much use of the standard template library, especially the main classes of string, vector, valarray, bitset, list, deque, map, set, pair, stack, priority_queue. These classes move C++ from a low-level language well on the way to a high-level language such as Matlab, with a good speed advantage.

Windows API (Win32 API)

Visual C++ has caught the interest of many programmers, but to my taste it is like opening pandora's box: suddenly vast quantities of source code that I did not write pop in to my program, and I immediately feel that (1) I have lost control over what my program is doing, (2) it has become unecessarily complicated and long, (3) various choices have been made that I do not want to make, (4) I am now dependent on Visual C++ to make even minor changes such as moving the position of a button on screen. Therefore I do not use Visual C++.

Follow this link for a simple introduction to Windows API, also called Win32 API.

I adopt the following style:

The main source file contains WinMain and usually also contains the main window's message handler, called WndProc. If the final executable name is "myprog" then the name of this source file is "myprog_main.cpp".

In WndProc, there is usually a long switch statement concerning all the various messages. I deal with messages in this order:

WM_CREATE
WM_SIZE
WM_PAINT
scrolling
WM_COMMAND (menus and buttons)
keyboard (WM_KEYDOWN, WM_CHAR)
mouse (WM_LBUTTONDOWN etc.)
WM_CLOSE
WM_DESTROY

If any of these takes more than a few lines of code, then put the code in a function, and the function is in some other source file. Each part of this switch statement should be complete, i.e. each part finishes with a return statement. The code in WndProc should be kept to a minimum. Don't forget that variables set by one message handler and used by another had better be declared static.

I mostly don't bother with visual resource editors, and therefore I have to pick my own id numbers for controls, menus etc. To save thinking about this, it helps to have some rules, and I follow: