Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

Phoenix C++ Coding Specification

Table of Contents

  1. Introduction
  2. A bit of philosophy
  3. Naming
    1. Attribute names
    2. Name syntax
  4. Properties of methods
  5. Exceptions
  6. Comments
    1. Doxygen
  7. Headers
    1. Header Guards
    2. Documentation
    3. What to include and when
  8. Classes
    1. Entity Classes
    2. Value-type Classes
    3. Implementation
    4. Class guidelines
  9. Acknowledgements

Introduction

Phoenix, by all accounts, is going to be a big code base. As a result, organization and style are much more important here than in typical small projects. As the number of lines crawls into the tens of thousands, bad style or poor design makes things inconvenient. As that number gets into the hundreds of thousands, it makes life outright unbareable. It's not clear that Phoenix will reach the millions of lines realm, but discipline is the only way it will survive that long. This article is meant to present the basic syntactic conventions that we should follow in the code. A brief addendum will follow that includes some information on basic design philosophy. That section is a companion to the / thread posted in the forums that details the Phoenix framework and basic components and concepts that should be used in designs.

A bit of philosophy

Syntactic conventions and frameworks exist for one major reason: consistency. Consistency allows everyone to look at each other's code and be able to read it and, hopefully, make changes easily and safely. As much as possible, try to solve design problems with solutions similar to those that have been used to solve previous problems. Follow design and syntax guidelines wherever possible. If you have a component whose job is similar to another component, try to give it a similar name. All of these things take extra time and discipline. They slow you down, and sometimes just frustrate you. But consistency, above all else, is favored over expediency.

One last piece of advice: program defensively. Always assume that the client will read as little documentation as they can before using your code. Hence, your method names, argument names, etc. should convey as much as possible about the code. Documentation is necessary and helpful, but the more you can describe your code using the code itself the better.

Naming

There are lots of names around. Class names, method names, etc. In every instance, try to choose a name that immediately conveys the purpose of an object or method. Phoenix's design is heavily object-oriented. Most objects represent a "real" object in the system we're simulating (i.e., the real world). So a good rule of thumb is, if the object has a real-world equivalent, use a word or name that somebody in the real world would recognize. Imagine somebody that has never seen a programming language before, but knows a great deal about the Air Force. If you've done a good job, they should be able to pick out objects from the code and understand what they're supposed to represent. e.g., "Aircraft" and "Squadron" are probably good names (assuming they represent what somebody would expect).

Attribute names

Phoenix is using "attribute-only" interface design. Thus, the names of your publicly accessible methods should be nouns, possibly with a number of adjectives. Again, choose a name that describes what that attribute would be in the real world. For Aircraft, you could have landingGearState(). Somebody that didn't know how to program, but knew that this was supposed to represent an attribute, should be able to guess what this attribute says about the aircraft. Moreover, they can probably guess what will happen if we change the state, and they can guess what information we can acquire by reading the state.

Name syntax

Include units in names if the unit is not standard

Most public interfaces and methods should use standard units. In Phoenix, we'll be using SI units.

insert specific units for each unit type here

If a member variable or internal method holds a value which is not in standard units for implementation reasons, document this by placing a unit name after the name. e.g.:

 double mThrustPounds; 

insert standard abbreviations for units

Don't use all upper-case abbreviations

If your name includes an abbreviation, capitalize the first letter if necessary, but follow it with lower case letters.

 WaasGps  mWaasGps;
double   mFuelOz; // Fuel, in ounces.  Not mFuelOZ.

In general, write out unit names if it can be afforded for short names. For the above example, the following is preferred:

 double  mFuelOunces; 

Class names

Class names should begin with a capital letter, with each subsequent word capitalized but with all other letters in lower-case. Class names cannot have spaces, and should not use _.

 class Aircraft;
class PhysicsEngine; 

Method names

Method names follow the same convention as class names, except that the first letter is not capitalized. Examples:

 class Square {
public:
 double area(void) const;
 void sideLength(double sideLength);
}; 

When a method represents an attribute of another attribute, name your attribute using the two attribute names, but order them so that the "primary" attribute name comes first. For instance, suppose that one conceptually has a "Rudder" attribute, but this is not stored as a single attribute. Instead, we've decided to provide the client with the current angle of the rudder directly (the 'angle' is an attribute of the higher-level 'rudder' attribute). In this case, an appropriate name is rudderAngle(), as this specifies the attribute 'rudder' first, followed by the attribute 'angle' that specifies which attribute of the rudder is being returned. This is preferred to angleOfRudder, etc. You can extend the interface to provide things such as rudderAngleMax() and rudderAngleMin(). Avoid alternatives such as maxRudderAngle and rudderMaxAngle that, aside from introducing inconsistency in naming, will also make it difficult to see all of the "rudder" attributes in the class when viewed in a list. If alphabetized (as will generally be the case when doxygen displays the methods), your methods corresponding to a single attribute ('rudder' in this case), will be grouped together. As you introduce max/min values for other attributes, this will become more valuable.

double elevatorAngle() const;
double elevatorAngleMax() const;
double elevatorAngleMin() const;
double rudderAngle() const;
double rudderAngleMax() const;
double rudderAngleMin() const;
compared to:
double elevatorAngle() const;
double maxElevatorAngle() const;
double maxRudderAngle() const;
double minElevatorAngle() const;
double minRudderAngle() const;
double rudderAngle() const;

Method arguments

Method arguments follow the same convention as method names. Use const-references to optimize places where a pass-by-value would otherwise be used.

 void textDescription(const String& textDescription); 

Note: this example has a parameter that shadows the method name. This is not a problem and emphasizes that the parameter is exactly the value to which the attribute represented by the method call will be set.

Avoid non-const references unless necessary. A client may not realize that the argument is a reference and may pass a value that they did not want modified. Example:

 class Clock {
  // sets time, stores old time in 'time'
  void time(uint32_t& time);
}; 

 // set both of our clocks to the time the user entered.
myClock.time(timeEnteredByUser);
myOtherClock.time(timeEnteredByUser); // d'oh!

The above code won't raise any eyebrows when somebody looks at it. The following hopefully makes the programmer duly suspicious:

 // set both of our clocks to the time the user entered.
myClock.time(&timeEnteredByUser);
myOtherClock.time(&timeEnteredByUser); 

Again: tell the client as much as you can by the way your code is called and used. In general, pointers indicate that you're passing access to your data to the code you're calling -- passing by value (or by const-reference, which appears equivalent to the caller) indicates that the called code cannot mess with your data.

Member variables

Variables which are members of an object should be prefixed with 'm'. This will distinguish them from other variables and parameters within the object's code. Example:

 class AircraftImpl : public Aircraft {
private:
  LandingGearState mLandingGearState;
  Newtons          mMaxGrossWeight;
}; 

Global variables

Global variables should be prefixed with a 'g' to indicate that they're global. In general, global variables and static data are major contributors to concurrency bugs and they hinder reuse of code. If you must use a global, be sure to document it along with any information about how and when it can be safely used. This is especially important if that global can be touched by code that is potentially running on multiple threads. Examples:

 uint32_t gThreadCount;
Lock gThreadCountLock; // Must hold this to read/write gThreadCount

Global and static constants

Global and static constants are another matter. These can be inside methods, in classes, or made globally accessible within a file. Like other constants, name them with all upper-case letters, separating words by '_' characters as in:

 const static double KILOGRAMS_PER_POUND = 0.45359237; 

#defines and Macro names

In C++ you should avoid macros and #define'd constants as much as possible. However, if you need to use them, name them with the same convention as for global constants:

 #define HAVE_PTHREAD_H 1
#define MAX(a, b) ((a) > (b) ? (a) : (b)) 

The MAX example should actually be implemented as an inline function:

 template <typename T>
inline const T& max(const T& a, const T& b) {
       return (a < b) ? b : a;
} 

Lock names

As demonstrated above, Lock and Semaphore objects should be named in a way that indicates what they're protecting. Frequently, locks protect a single value. In this case, name the lock identically to the value it protects, followed by "%Lock" (gThreadCountLock protects gThreadCount, in the example previously). If a semaphore protects a value like a lock, follow the same convention but follow the name with "Sem". If the semaphore is used to keep track of a value (e.g., the number of some resource available) try to choose a name that indicates this:

 Semaphore mAvailableSlotsSem; 

When the client sees a call like "mAvailableSlotsSem.wait();" it should be fairly clear what the client is waiting for.

C-style functions

As with macros, C-style functions should be few. But, in case you should come across one, name it with the GNU convention of all lower-case letters separating words by '_'.

 void init_legacy_code(void); 

Enum names

For VERY simple enumerative types (more complex types should be developed into value-type classes), enums can be used. Name them as you would name a class of the same type. The enum labels should be named using the same convention as constants. If the enum is not contained within a scope (e.g., within a class or a private namespace) you should prefix the label names with a name or abbreviation that identifies the enum to which it belongs. In a private scope:

 enum TaskState {
     IDLE,
     WAITING,
     RUNNING
}; 

However, if the enum is in a scope where conflicts could occur, prefix the names:

 enum TaskState {
     TASK_STATE_IDLE,
     TASK_STATE_WAITING,
     TASK_STATE_RUNNING
}; 

Enums offer only minimal type-safety. Keep this in mind.

Properties of methods

Methods within public interfaces will follow the attribute-only interface style. Attribute methods have a handful of properties that they should follow. The properties are described below. If your method supports a property that is not necessary but potentially useful to the caller, document it. MUCH more importantly: if your method DOES NOT satisfy one of the properties that the client expects, document which one(s) it does not follow. Many high-level calls simply can't support all of these -- e.g., transactionality of "running" the simulation would require backing up the entire sim state and then reverting to the previous frame state if something failed. This is impractical, so we just document that the method which does this is non-transactional. The properties:

Nilpotence

Calling the method one or more times successively is logically the same as not calling it at all.

This does NOT mean that the call must be const. The method is allowed to make internal changes to the state (for instance, to set up lazily-allocated structures) but the logical state of the object as the client sees it should not change. Read accessors should be nilpotent.

 // myObject is in some state here.
name = myObject->name();
name = myObject->name();
name = myObject->name();
// myObject's _logical_ state has not visibly changed

Idempotence

Calling the method one or more times successively with the same arguments is the same as calling it just once.

Write accessors should be idempotent. This means that "setting" an attribute to the same value several times in a row is the same as just setting it once. Again, internal modifications are fine, but the state as the client sees it should not change.

 myObject->name(newName); // set name of my object.
// myObject is in some state here.
myObject->name(newName);
myObject->name(newName);
// myObject's _logical_ state has not visibly changed. 

Transactional

Calling the method is an "all or nothing" proposition. Either the method completes its operation and returns successfully, or it does nothing and signals an error. If the error occurs in the middle of the operation, the method will return the object to its previous state.

All read/write accessors should be transactional if possible. Read accessors generally should not fail, but if an internal operation fails, then the object should be returned to its former state and something returned to the client to indicate that the value couldn't be read.

A good way to make transactionality easier is to check for ways that an operation might fail before doing anything. Check client supplied parameters, safely acquire resources, and only after all of these checks succeed, proceed with modifications. If any of these things fail, then you can signal an error and return without having to restore any state.

Exception free

The method does not throw exceptions.

Generally we already document what exceptions a method might throw. However, if you expect your method to be exception free, double check that it doesn't call other non-exception-free methods that could throw an exception that passes up through your code.

Read accessors should be exception free.

Exceptions

Phoenix will use exceptions to signal errors rather than classical error-code handling. Exceptions are powerful tools, but like all power tools, they can hurt you. Try to write exception-savvy code.

Always use the global exception types

Phoenix includes a header (Phx/Util/PhxExceptions.h) which defines a handful of exception types. Any exception that is thrown should be one of the specific types listed there (or be derived from one if additional specificity is desired). The Phoenix "base" exception type (Exception) should not be thrown or derived from. Use the "leaf" types that are derived from it. If you want to throw an exception that is not among these types, propose its addition to the group. Don't create your own exception types!

The reason is that we want exception types to be understood by callers. Not just the caller that is directly responsible for calling your code -- but ALL the callers in the call stack. It's almost always the case that thrown exceptions end up at the top level of the program because they were not caught by other callers. The top-level program cannot possibly know about all of the custom exception types lurking in the underlying code, but it still wants to make as much sense of it as possible so that it can take corrective action if possible or provide information for programmers.

Destructors don't throw exceptions

Destructors should not throw exceptions EVER. If the program is trying to delete an object, it probably has no interest in or even the capability for dealing with the exception. Exceptions thrown from destructors often cause memory leaks, and end up resulting in exceptions being thrown from unusual places where they are not expected. In short: destruction of objects should never fail. If you encounter a problem, log an error for the programmer and move on.

Copy constructors should not throw exceptions

Copy constructors, if possible, should not throw exceptions. This is hard to guarantee in general because memory allocation can frequently be required for copying. Nonetheless, you should avoid making a habit of throwing any other exceptions. Copy constructors are invoked in any "pass by value" situation, and having an exception thrown from within these constructs is confusing and can also lead to memory leaks.

Do not use the exception specification feature

Originally, we had planned to use C++ exception specifications. It turns out that these add a substantial overhead and provide nearly zero benefit. The compiler apparently inserts "try-catch" blocks within the code to catch any exceptions not declared in the throw(..) specification of a method. If an unexpected exception is caught, the compiler just calls the installed unexpected() handler (which, by default, aborts the program). These specifications are not checked at compile time.

Making your code exception savvy

Three rules for making your code (and the code that calls it!) exception-savvy:

 Ptr<MyObject> myObject = new MyObject();
// do something that throws exceptions 

If an exception is thrown, myObject will automatically be freed. Similarly, you should ALWAYS acquire locks using the LockHolder or Read/WriteLockHolder objects. These will ensure that your code automatically releases locks if an exception is thrown.

Comments

Doxygen

Phoenix will use Doxygen to generate documentation from commented code. All headers and some source files will contain documentation. Documentation comments begin with /** and end with */. You can read more about Doxygen and the commands that can be placed within comments at the doxygen homepage: http://www.stack.nl/~dimitri/doxygen/

At least, your comments for public interfaces should contain the following:

When possible, refer to other classes by their actual name in the documentation. Doxygen will recognize these and place links to their accompanying docs page for you automatically.

The notations below are for our own documenting purposes and may also be parsed into doxygen documentation in the future. Use them now when possible.

Hacks and kludges

Sooner or later there are little pieces of messy code that are put in for various reasons. Be sure to document these within your implementation. Use @hack to denote a location in the code where something is hacked and should be cleaned up at a later time if possible.

Places for optimization

Put @optimization in comments to indicate a place where optimizations could be performed but were left out in order to get code working properly first or for lack of time. If that section of code is later found to be executing frequently, these marked areas will be easily picked up and optimized. This is also helpful when the author of the code sees a possible optimization that would be unclear to a future developer.

Other useful doxygen tags

The following tags are already supported by doxygen and should be used wherever appropriate:

Headers

Header Guards

Always use header guards to prevent multiple inclusion

Every header should include a preprocessor guard like the following:
 #ifndef NAMESPACE_FILENAME_H
#define NAMESPACE_FILENAME_H
// your code here
#endif // NAMESPACE_FILENAME_H

NAMESPACE should be replaced with the namespace name (in all caps) of the namespace that will enclose the contents included by the header. Since Phoenix uses the "Phx" namespace for its classes, Phoenix headers always look like:

 #ifndef PHX_FILENAME_H
#define PHX_FILENAME_H
// you code here
#endif // PHX_FILENAME_H

FILENAME is replaced with the remaining name of the header file. For instance, the file PhxAircraft.h would use PHX_AIRCRAFT_H for its guard.

Don't use leading __ or _ in any guard macros

Aside from not following the convention, these violate standards. All names beginning with __ and _ are reserved for standards bodies and compiler implementors (respectively).

Documentation

All header files in the include/ directories should be documented. To get doxygen to pick up the file, the header should include a documentation comment whose first line contains @file <filename> where <filename> is the exact name of the header file. Following should be at least a brief description of the header file's contents.

What to include and when

Include only what you need

Do your best to include the minimal set of header files necessary for your source code to work. This improves build times and reduces the number of files that must be rebuilt after a header is modified. It also helps to indicate what kinds of objects a reader should expect your module to be using. Doxygen generates a list of included files for each documented source file; if you include all the headers even when you don't need them, a reader will not know immediately what other sources your code actually depends on.

Using precompiled headers

Some compilers allow headers that are frequently included but not modified to be precompiled to improve performance. You can include the precompiled header, but you should subsequently include the individual headers. The precompiled headers may not be used on some platforms and thus its inclusion may be ignored -- leaving you without the headers you were expecting. Including them explicitly afterward guarantees that they're included into your file no matter what. If the precompiled header exists then any headers included later will ignored because of the header guards. Even better, if new headers are added into the precompiled list, those headers will automatically take the place of subsequently included headers without modification to your code.

To be sure your code compiles correctly without the precompiled headers try compiling it without the precompiled headers included. If that works, add in the precompiled header to get the boost in build speed.

 #include <Phx/PhxPrecompiledHeaders.h> // include anything precompiled (optional)
#include <Phx/Core/PhxTask.h> // include files we actually need
#include <Phx/Util/PhxLocks.h>

Classes

Entity Classes

Most "high level" objects in the simulator should not be copied or assigned -- they are entities that represent real world objects or help simulate real world objects. Objects of this kind are called "Entities", and their distinguishing characteristic is that they have a sense of identity. An Aircraft, for instance, is an object in the real world. We can point to a single aircraft and know that it is the only thing occupying that space at that time. We could "duplicate" it by building a new aircraft but these are not really the same aircraft. They are two different aircraft of the same type. We can refer to aircraft but we cannot copy them or assign them to be equivalent to some other aircraft -- this doesn't make sense in our system (or in reality), so we don't allow it in the code. Hence, the distinguishing, mechanical, characteristic of an entity is that it cannot by assigned or copied. They are always passed by pointer and never by value. Some objects have this characteristic even though it's not clear that they represent an object with "identity" in the real world. However, they are considered entities nonetheless because of the way they interact with other objects in the code. The distinction will become more clear when we present value types below.

Entity layout

Entity classes are not copy-constructable or assignable. They always follow the attribute-only interface and generally hide their implementation rigorously from the client. See the design guide discussion: Separate interfaces and implementation. Below is a small template for an entity class which derives from the public NamedInterface base class (more on this in the design guide):

#include <Phx/PhxConfig.h>
#include <Phx/PhxTypes.h>
#include <Phx/Core/PhxNamedInterface.h>

class MyEntity : public NamedInterface {
public:
  virtual ~MyEntity(); // ALWAYS have a virtual destructor

  // public attribute methods here
  virtual void attribute(...) = 0;
  virtual ... attribute(void) = 0;

protected:
  // setAttribute(...) style methods to allow implementations
  // and subclasses to set any inline attributes.  If you have no
  // inline accessors, then this section is not needed.

private:
  MyEntity(MyEntity& ); // no copy
  MyEntity& operator=(MyEntity&); // no assignment
}; 

The NamedInterface class also derives from the LockedPtrInterface class which allows us to pass smart pointers to entities (see documentation for Ptr and LockedPtrInterface).

Entity type examples

The following are examples of things you would implement as Entity types:
Aircraft PhysicsEngine Clock GUI manager and GUI elements
Clouds Squadron Campaign manager Sky

Value-type Classes

A value type class is one that is distinguished primarily by assignability. Unlike an entity, it does NOT represent a "thing" in our world, or even a logical object that helps to simulate the world. It has no identity. Two objects with the same "value" are, logically, the same object. A simple example is the 'int' type. This is a value type (though not a class). When two integers are equal, they might as well be the same int. There is absolutely no difference between them. Contrast this with Aircraft. If two aircraft are "equal" because they're the same type, color and (oddly) have the same tail number, there is still a difference between them -- they're two separate aircraft in the simulation. Allowing multiple copies of an aircraft to represent one aircraft would be awfully confusing! Likewise, for engines like "PhysicsEngine" that aren't "real world" objects, they still have identity within our system. We can't simply "throw away" a physics engine because we've got another that is apparently "equal" to it. Value types, on the other hand, are just the opposite. If two objects of type Matrix are equal, then we can mix them up, throw one away or whatever and there is zero difference between them.

Value type layout

Value types that wrap primitive types (like int or double) can be defined with the ValueType template to add type safety. In general, though, you can define value types like any other classes.

Value types should also use the attribute-only interface. However, they need not (in fact usually won't) derive from the PtrInterface or LockedPtrInterface and often use inline implementation extensively for maximum speed. Value types can be passed by value at will (though const-references should be used for large value types as an optimization). They also need not have a virtual destructor, which means that the class doesn't have to have a virtual table, and hence is smaller and more efficient. (Be aware, however, of the consequences that not having a virtual destructor implies!)

class MyValueType {
public:
    MyValueType() {
     // default construction
    }
    MyValueType(const MyValueType& value) {
     // copy construction
    }

    ~MyValueType() { // non-virtual destruction
    }
    
    const MyValueType& operator=(const MyValueType& rhs) {
       // assignment
       return *this;
    }
    
    // comparison -- should be consistent with operator=
    bool operator==(const MyValueType& rhs) const {
    }

    // other arithmetic operators if necessary and EASILY UNDERSTOOD

    // other attribute interfaces

private:
    // private data
}; 

Value type examples

The following are examples of things you would consider to be value types.
int Matrices Complex numbers Physical units (Kilograms, Time, etc.)
STL collections Coordinate frames Exceptions Log entries

Implementation

Thus far, the policy on implementing classes is fairly loose. You may break from the attribute-only interface but you should keep with the general conventions outlined in this document whenever there is not a compelling reason to be inconsistent. New members or future developers may need to change, debug, or develop modules that cooperate with your code -- make their lives as easy as you can (and you'll probably make your own life easier in the future too).

Class guidelines

Some other guidelines for your classes follow.

Accessory types

Many of your classes will have accessory types. These are objects that exist to help your class do its work. These types might be subinterfaces (see subinterfaces), a Manager class to keep track of your objects, or perhaps a small value-type used to represent different kinds of values that your class uses. In every case, you should define your accessory type within the scope of its owning class. In other words, if your class is MyClass and it has an accessory type Accessory, then the following is appropriate:
class MyClass {
public:
    class Accessory; // declare accessory type here, in MyClass
};

// Accessory type, scoped within MyClass
class MyClass::Accessory {
};

Avoid putting your accessory types in the top-level namespace. As accessory types become more complicated they may have their own accessory types, and organizing them will be more important. For instance, a Manager might have its own set of value types or other accessories. It's clear from the language which class the object MyClass::MyManager::Accessory is serving. This has the slight caveat that subclasses of "MyClass" will now have "MyManager" embedded within them (i.e., MySubclass::MyManager refers to MyClass::MyManager). Sometimes this is good, and other times not.

Thread safety: Value types vs. Entities

Entities are identifiable objects that occupy a sufficiently "high level" position in our system that we expect that they are capable of fending for themselves in a threaded environment. For this reason, if your entity wants to use the smart Ptr interface (and it ought to!) it should always derive from the LockedPtrInterface class. Since entities should usually derive from NamedInterface, this requirement is met for you. Value types, however, are the opposite. They're meant to be fast, generic, and aren't expected to be capable of protecting themselves in a threaded system. As such, when you create a value type, you don't need to make it thread safe. Hence, for best efficiency, you may derive it from the PtrInterface class to forego the locking overhead involved in LockedPtrInterface if you plan on using that value type with smart pointers (but see the warning below!) As a client you should never assume that a value type is thread safe. So if you plan on using them in a piece of code (e.g., in an entity) where threading is an issue, you must protect them externally. This goes for all value types, which include everything from integers, matrices and coordinate frames to STL collections.

A final word of warning: using PtrInterface makes all of your pointers to a single object non-thread-safe. Thus, the following is not safe:

// Initialization (not threaded)
Ptr<Foo> a = new Foo();  // create a new Foo
Ptr<Foo> b = a; // create a second pointer to *a

// thread 1
Ptr<Foo> c = a; // okay so far;  calls a->newReference();

// thread 2
Ptr<Foo> d = b; // Calls b->newReference() which is a->newReference();
// and newReference() is NOT thread safe!!

As the above snippet demonstrates, even if you're not using the same pointers on different threads, you cannot manipulate pointers to the same object safely. Thus, if a Ptr is given back to a client, then there must be some guarantee that the client will never attempt to copy or release that pointer concurrently. This is really hard to guarantee! This is one reason to return objects by value and only return Ptr's when absolutely necessary. If the value-type is too large to be returned/passed by value, but you have a copy of it within your object, you could return a const reference to it. Deriving from PtrInterface is more to allow implementors to use the smart Ptr in their private code. You should be wary of using smart-pointers to value types in your public interfaces -- it's a jungle out there.

Order of public/protected/private sections

The public section of your classes should generally be first. This is the interface that a reader wants to see. Protected interfaces are next, followed by the private section.

Initialize all variables

Always initialize member variables in your classes. Even if you expect them to be set by other methods called by the caller, set them to a default value. The performance cost is negligable.

Be const-correct

Make your methods "const correct". This means using const for parameters which will not be modified and qualifying the method as const when it will not modify 'this'. Constness is an opt-in proposition and hence requires some effort. However, it is valuable and unless everyone makes an effort it will be difficult to use this feature to its full effect.

Don't overload operators unnecessarily

C++ gives you the power to overload many many operators. There is just enough rope here for you to hang yourself. Do NOT overload operators unless it is absolutely clear that the operation is intrinsic to the type of object you're implementing and it will be completely clear to everyone what the operators do. operator+ for a Matrix class is reasonable, but is not reasonable for a hash-table (probably adds an object? Maybe?) operator-> is reasonable only for a pointer class or, perhaps, an STL-style iterator. There should, for other reasons, be only one pointer class (Ptr) -- so there will be few good reasons to use this or the unary version of operator*. In general, unless you have a bullet-proof reason for using the operators, use other mechanisms.

Value types versus typedefs

Typedefs are not type safe. They only define an "alias" for a type name. Thus, two objects typedef'd as integers can be assigned to each other by accident even when they have nothing to do with each other. To avoid this, define classes for your value types (and use the ValueType template if you just want to wrap a primitive type). This will make them full-fledged classes with all of the type safety benefits. The performance impact is nearly zero if you inline all of your methods (as the ValueType template does).

Acknowledgements

This document and the methodology presented within are based on material from the CS249 and CS349 programming courses (and the accompanying course notes) at Stanford Univ. taught by David R. Cheriton (Sep 2004 - Mar 2005).

Additional influence from the following document by Todd Hoff:
http://www.possibility.com/Cpp/CppCodingStandard.html


Generated on Mon Jul 10 19:45:29 2006 for Phoenix OSFS by  doxygen 1.4.2