Documente Academic
Documente Profesional
Documente Cultură
tmh@possibility.com / http://www.possibility.com/Tmh
Using this Standard. If you want to make a local copy of this
standard and use it as your own you are perfectly free to do so.
That's why I made it! If you find any errors or make any
improvements please email me the changes so I can merge them
in. I also have a programming blog at
http://radio.weblogs.com/0103955/categories/stupidHumanProgra
mming/ that is accidently interesting at times, as is my wiki at
http://www.possibility.com/epowiki/Wiki.jsp.
Contents
Introduction ...............................................................................................................................................5
Standardization is Important ................................................................................................................5
Standards Enforcement .........................................................................................................................5
Accepting an Idea .................................................................................................................................6
6 Phases of a Project .............................................................................................................................6
Flow Chart for Project Decision Making .............................................................................................6
Leadership ............................................................................................................................................7
Resources- Take a Look! ..........................................................................................................................8
General .................................................................................................................................................8
Book Recommendations .......................................................................................................................8
Names ........................................................................................................................................................9
Make Names Fit ...................................................................................................................................9
Include Units in Names ......................................................................................................................10
No All Upper Case Abbreviations ......................................................................................................10
Class Names .......................................................................................................................................10
Class Library Names ..........................................................................................................................11
Method Names ...................................................................................................................................11
Class Attribute Names ........................................................................................................................11
Method Argument Names ..................................................................................................................12
Variable Names on the Stack .............................................................................................................12
Pointer Variables ................................................................................................................................13
Reference Variables and Functions Returning References.................................................................13
Global Variables .................................................................................................................................14
Global Constants ................................................................................................................................14
Static Variables ...................................................................................................................................14
Type Names ........................................................................................................................................14
Enum Names ......................................................................................................................................15
#define and Macro Names ..................................................................................................................16
C Function Names ..............................................................................................................................16
C++ File Extensions ...........................................................................................................................16
Documentation ........................................................................................................................................18
Comments Should Tell a Story ..........................................................................................................18
Document Decisions ...........................................................................................................................18
Use Extractable Headers ....................................................................................................................18
Comment All Questions a Programmer May Have When Looking at Your Code ............................18
Make Your Code Discoverable by Browsing .....................................................................................19
Write Comments as You Code ...........................................................................................................19
Make Gotchas Explicit .......................................................................................................................20
Interface and Implementation Documentation ...................................................................................21
Directory Documentation ...................................................................................................................21
Include Statement Documentation .....................................................................................................22
Block Comments ................................................................................................................................22
Complexity Management ........................................................................................................................24
Layering ..............................................................................................................................................24
Minimize Dependencies with Abstract Base Classes .........................................................................24
Liskov's Substitution Principle (LSP) ................................................................................................26
Open/Closed Principle ........................................................................................................................26
Register/Dispatch Idiom .....................................................................................................................27
Delegation ..........................................................................................................................................27
Follow the Law of Demeter ................................................................................................................28
Design by Contract .............................................................................................................................29
Classes .....................................................................................................................................................30
Naming Class Files..............................................................................................................................30
Class Layout .......................................................................................................................................30
What should go in public/protected/private? ......................................................................................33
Prototype Source File .........................................................................................................................33
Use Header File Guards .....................................................................................................................34
Required Methods for a Class ............................................................................................................35
Method Layout ...................................................................................................................................36
Formatting Methods with Multiple Arguments ..................................................................................37
Different Accessor Styles ...................................................................................................................37
Init Idiom for Initializing Objects ......................................................................................................39
Initialize all Variables ........................................................................................................................40
Minimize Inlines .................................................................................................................................40
Think About What Work to do in Constructors .................................................................................41
Don't Over Use Operators ..................................................................................................................42
Thin vs. Thick Class Interfaces ..........................................................................................................42
Short Methods ....................................................................................................................................42
In a Source file Indicate if a Method is Static or Virtual ...................................................................43
Process ....................................................................................................................................................44
Use a Design Notation and Process ....................................................................................................44
Using Use Cases .................................................................................................................................44
Using Stories ......................................................................................................................................45
Unified Modeling Language ..............................................................................................................46
Code Reviews .....................................................................................................................................46
Create a Source Code Control System Early and Not Often ..............................................................46
Create a Bug Tracking System Early and Not Often .........................................................................48
RCS Keywords, Change Log, and History Policy .............................................................................48
Honor Responsibilities .......................................................................................................................49
Process Automation ............................................................................................................................50
Tools Agreement ................................................................................................................................51
Non-Blocking Scheduling ..................................................................................................................51
Using Personas ...................................................................................................................................51
Use a Continuous Build System..........................................................................................................52
Code in the Dominant Style................................................................................................................52
Run Unit Tests Before Every Check-in...............................................................................................52
Formatting ...............................................................................................................................................53
Braces {} Policy .................................................................................................................................53
Indentation/Tabs/Space Policy ...........................................................................................................54
Parens () with Key Words and Functions Policy ...............................................................................55
A Line Should Not Exceed 78 Characters ..........................................................................................56
If Then Else Formatting .....................................................................................................................56
switch Formatting ...............................................................................................................................57
Use of goto,continue,break and ?: ......................................................................................................57
One Statement Per Line ......................................................................................................................59
Alignment of Declaration Blocks .......................................................................................................59
Include static and virtual Key Words in Source File ..........................................................................59
Document Null Statements .................................................................................................................60
Exceptions ...............................................................................................................................................61
Create One Exception for Each Library .............................................................................................61
Selecting Between Exceptions And Asserts .......................................................................................61
Be Careful Throwing Exceptions in Destructors ...............................................................................64
Working With Libaries Using All Sorts of Different Policies ...........................................................64
Templates ................................................................................................................................................66
Namespaces .............................................................................................................................................67
Create Unique Name Space Names ....................................................................................................67
Don't Globally Define using ...............................................................................................................67
Create Shortcut Names .......................................................................................................................68
Miscellaneous ..........................................................................................................................................69
Be Const Correct ................................................................................................................................69
Placement of the Const Qualifier .......................................................................................................70
Use Streams ........................................................................................................................................70
No Magic Numbers ............................................................................................................................71
Error Return Check Policy .................................................................................................................71
To Use Enums or Not to Use Enums .................................................................................................72
Macros ................................................................................................................................................72
Do Not Default If Test to Non-Zero ...................................................................................................73
The Bull of Boolean Types ................................................................................................................74
Usually Avoid Embedded Assignments .............................................................................................75
Reusing Your Hard Work and the Hard Work of Others ...................................................................75
Commenting Out Large Code Blocks ................................................................................................76
Use #if Not #ifdef ...............................................................................................................................77
Creating a C Function in C++ ............................................................................................................77
Mixing C and C++ ..............................................................................................................................78
No Data Definitions in Header Files ..................................................................................................78
Make Functions Reentrant ..................................................................................................................79
Use the Resource Acquisition is Initialization (RAII) Idiom .............................................................79
Remove Trailing Whitespace .............................................................................................................79
Portability ................................................................................................................................................80
Use Typedefs for Types .....................................................................................................................80
Alignment of Class Members .............................................................................................................80
Compiler Dependent Exceptions ........................................................................................................80
Compiler Dependent RTTI .................................................................................................................80
Popular Myths .........................................................................................................................................81
Promise of OO ....................................................................................................................................81
You can't use OO and C++ on Embedded Systems ...........................................................................81
Introduction
Standardization is Important
It helps if the standard annoys everyone in some way so everyone feels they are on the same playing
field. The proposal here has evolved over many projects, many companies, and literally a total of many
weeks spent arguing. It is no particular person's style and is certainly open to local amendments.
Good Points
When a project tries to adhere to common standards a few good things happen:
• Programmers can go into any code and figure out what's going on.
• New people can get up to speed quickly.
• People new to C++ are spared the need to develop a personal style and defend it to the death.
• People new to C++ are spared making the same mistakes over and over again.
• People make fewer mistakes in consistent environments.
• Programmers have a common enemy :-)
Bad Points
Now the bad:
• The standard is usually stupid because it was made by someone who doesn't understand C++.
• The standard is usually stupid because it's not what I do.
• Standards reduce creativity.
• Standards are unnecessary as long as people are consistent.
• Standards enforce too much structure.
• People ignore standards anyway.
• Standards can be used as a reason for NIH (not invented here) because the new/borrowed code
won't follow the standard.
Discussion
The experience of many projects leads to the conclusion that using coding standards makes the project
go smoother. Are standards necessary for success? Of course not. But they help, and we need all the
help we can get! Be honest, most arguments against a particular standard come from the ego. Few
decisions in a reasonable standard really can be said to be technically deficient, just matters of taste. So
be flexible, control the ego a bit, and remember any project is fundamentally a team effort.
Standards Enforcement
First, any serious concerns about the standard should be brought up and worked out within the group.
Maybe the standard is not quite appropriate for your situation. It may have overlooked important issues
or maybe someone in power vehemently disagrees with certain issues :-)
In any case, once finalized hopefully people will play the adult and understand that this standard is
reasonable, and has been found reasonable by many other programmers, and therefore is worthy of
being followed even with personal reservations.
Failing willing cooperation it can be made a requirement that this standard must be followed to pass a
code inspection.
Failing that the only solution is a massive tickling party on the offending party.
Accepting an Idea
1. It's impossible.
2. Maybe it's possible, but it's weak and uninteresting.
3. It is true and I told you so.
4. I thought of it first.
5. How could it be otherwise.
If you come to objects with a negative preconception please keep an open mind. You may still
conclude objects are bunk, but there's a road you must follow to accept something different. Allow
yourself to travel it for a while.
6 Phases of a Project
1. Enthusiasm
2. Disillusionment
3. Panic
4. A Search for the Guilty
5. The Punishment of the Innocent
6. Praise and Honor for the Non-Participants
Leadership
I wish i had said this, but it was said by asd@asd.com in comp.software-eng.
Leaders:
1. lead by example
2. don't ask anything of anyone they wouldn't do themselves
3. are called on to make difficult and unpopular decisions
4. keep the team focused
5. reward/support their team in whatever they do
6. keep/clear unnecessary crap out of the way of the team
Consensus is great. If it lasts for the project lifecycle, consider yourself blessed. I've been on a couple
projects where two engineers just blantantly disagreed! They were always:
Programmer #1 says " x = 1"
Programmer #2 says " x != 1"
That's when a Project Leader is required. Unless you want to flip a coin.
Oh yah - one more thing. Project leaders: TAKE the blame when things go wrong and SHARE the
credit when things go right.
Ain't easy - but it's the way I try to run my life.
Resources- Take a Look!
General
• Code Reviews. If you are interested in coding standards you may also be interested in Code
Review Standards I have created at http://www.possibility.com/epowiki/Wiki.jsp?
page=CodeReviews.
• Design Stories
• OO Info Sources
• Designing Qt-Style C++ APIs - A good paper on API I design for C++. I don't agree with
everything, but it is good.
• Unified Modeling Language (UML)
• OO FAQ - All You Wanted to Know About OO
• C++ FAQ LITE
• C++ Source Libraries
• C++ Tutorials
• ACE C++ Library
• Collection of Other Standards
• Design by Contract from Eiffle
• C++ isn't Perfect, Here are Some Reasons Why
• Doxygen - is a 'javadoc' like utility that extracts comments and relevant information from your
C++/C programs and generates HTML pages from it.
• Const Correctness - A very nice article on const correctness by Chad Loder.
• Abraxis Code Check - A program for checking code for coding standard violations and other
problems.
Book Recommendations
What are some good C++ books you can buy for you and your team?
1. Koenig/Moo's "Accelerated C++"
2. Lippman/Moo's "C++ Primer" 4th Edition
3. Bruce Eckel's "Thinking In C++"
4. Scott Meyers "Effective C++"
5. Dewhurst's "C++ Gotchas"
6. Meyers' "Effective STL"
7. Josuttis' "The C++ Standard Library"
8. Vandevoorde/Josuttis' "C++ Templates"
9. Langer/Kreft's "Standard C++ IOStreams and Locales"
10.Sutter's "Exceptional C++"
11.Sutter's "More Exceptional C++ and Exceptional C++ Style"
12.Martin's "Agile Software Development: Principles, Patterns, and Practices"
Names
Make Names Fit
Names are the heart of programming. In the past people believed knowing someone's true name gave
them magical power over that person. If you can think up the true name for something, you give
yourself and the people coming after power over the code. Don't laugh!
A name is the result of a long deep thought process about the ecology it lives in. Only a programmer
who understands the system as a whole can create a name that "fits" with the system. If the name is
appropriate everything fits together naturally, relationships are clear, meaning is derivable, and
reasoning from common human expectations works as expected.
If you find all your names could be Thing and DoIt then you should probably revisit your design.
Class Names
• Name the class after what it is. If you can't think of what it is that is a clue you have not thought
through the design well enough.
• Compound names of over three words are a clue your design may be confusing various entities
in your system. Revisit your design. Try a CRC card session to see if your objects have more
responsibilities than they should.
• Avoid the temptation of bringing the name of the class a class derives from into the derived
class's name. A class should stand on its own. It doesn't matter what it derives from.
• Suffixes are sometimes helpful. For example, if your system uses agents then naming something
DownloadAgent conveys real information.
Better yet is to make a variable into a class so bad conversions can be caught.
Justification
• People seem to have very different intuitions when making names containing abbreviations. It's
best to settle on one strategy so the names are absolutely predictable.
Take for example NetworkABCKey. Notice how the C from ABC and K from key are confused.
Some people don't mind this and others just hate it so you'll find different policies in different
code so you never know what to call something.
Example
class FluidOz // NOT FluidOZ
class NetworkAbcKey // NOT NetworkABCKey
Class Names
• Use upper case letters as word separators, lower case for the rest of a word
• First character in a name is upper case
• No underbars ('_')
Justification
• Of all the different naming strategies many people found this one the best compromise.
Example
class NameOneTwo
class Name
Class Library Names
• Now that name spaces are becoming more widely implemented, name spaces should be used to
prevent class name conflicts among libraries from different vendors and groups.
• When not using name spaces, it's common to prevent class name clashes by prefixing class
names with a unique string. Two characters is sufficient, but a longer length is fine.
Example
John Johnson's complete data structure library could use JJ as a prefix, so classes would be:
class JjLinkList
{
}
Method Names
• Use the same rule as for class names.
Justification
• Of all the different naming strategies many people found this one the best compromise.
Example
class NameOneTwo
{
public:
int DoIt();
void HandleError();
}
Justification
• Prepending 'm' prevents any conflict with method names. Often your methods and attribute
names will be similar, especially for accessors.
Example
class NameOneTwo
{
public:
int VarAbc();
int ErrorNumber();
private:
int mVarAbc;
int mErrorNumber;
String* mpName;
}
Justification
• You can always tell which variables are passed in variables.
• You can use names similar to class names without conflicting with class names.
Example
class NameOneTwo
{
public:
int StartYourEngines(
Engine& rSomeEngine,
Engine& rAnotherEngine);
}
Justification
• With this approach the scope of the variable is clear in the code.
• Now all variables look different and are identifiable in the code.
Example
int
NameOneTwo::HandleError(int errorNumber)
{
int error= OsErr();
Time time_of_error;
ErrorProcessor error_processor;
Time* p_out_of_time= 0;
}
The standard pointer notation is not entirely satisfactory because it doesn't look quite right, but it is
consistent.
How do you handle statics? There's never a reason to have a static local to a function so there's no
reason to invent a syntax for it. But like for most absolute rules, there is an exception, that is when
making singletons. Use a "s_" prefix in this case. Take a look at Singleton Pattern for more details.
Pointer Variables
• pointers should be prepended by a 'p' in most cases
• place the * close to the pointer type not the variable name
Justification
• The idea is that the difference between a pointer, object, and a reference to an object is
important for understanding the code, especially in C++ where -> can be overloaded, and
casting and copy semantics are important.
• Pointers really are a change of type so the * belongs near the type. One reservation with this
policy relates to declaring multiple variables with the same type on the same line. In C++ the
pointer modifier only applies to the closest variable, not all of them, which can be very
confusing, especially for newbies. You want to have one declaration per line anyway so you can
document each variable.
Example
String* pName= new String;
Justification
• The difference between variable types is clarified.
• It establishes the difference between a method returning a modifiable object and the same
method name returning a non-modifiable object.
Example
class Test
{
public:
void DoSomething(StatusInfo& rStatus);
StatusInfo& rStatus();
const StatusInfo& Status() const;
private:
StatusInfo& mrStatus;
}
Global Variables
• Global variables should be prepended with a 'g'.
Justification
• It's important to know the scope of a variable.
Example
Logger gLog;
Logger* gpLog;
Global Constants
• Global constants should be all caps with '_' separators.
Justification
It's tradition for global constants to named this way. You must be careful to not conflict with other
global #defines and enum labels.
Example
const int A_GLOBAL_CONSTANT= 5;
Static Variables
• Static variables may be prepended with 's'.
Justification
• It's important to know the scope of a variable.
Example
class Test
{
public:
private:
static StatusInfo msStatus;
}
Type Names
• When possible for types based on native types make a typedef.
• Typedef names should use the same naming policy as for a class with the word Type appended.
Justification
• Of all the different naming strategies many people found this one the best compromise.
• Types are things so should use upper case letters. Type is appended to make it clear this is not a
class.
Example
typedef uint16 ModuleType;
typedef uint32 SystemType;
Enum Names
Labels All Upper Case with '_' Word Separators
This is the standard rule for enum labels.
Example
enum PinStateType
{
PIN_OFF,
PIN_ON
};
Example
enum PinStateType If PIN was not prepended a conflict
{ would occur as OFF and ON are probably
PIN_OFF, already defined.
PIN_ON
};
Example
enum { STATE_ERR, STATE_OPEN, STATE_RUNNING, STATE_DYING};
#define and Macro Names
• Put #defines and macros in all upper using '_' separators.
Justification
This makes it very clear that the value is not alterable and in the case of macros, makes it clear that you
are using a construct that requires care.
Some subtle errors can occur when macro names and enum labels use the same name.
Example
#define MAX(a,b) blah
#define IS_ERR(err) blah
C Function Names
• In a C++ project there should be very few C functions.
• For C functions use the GNU convention of all lower case letters with '_' as the word delimiter.
Justification
• It makes C functions very different from any C++ related names.
Example
int
some_bloody_function()
{
}
Document Decisions
Comments should document decisions. At every point where you had a choice of what to do place a
comment describing which choice you made and why. Archeologists will find this the most useful
information.
Gotcha Keywords
• :TODO: topic
Means there's more to do here, don't forget.
• :BUG: [bugid] topic
means there's a Known bug here, explain it and optionally give a bug ID.
• :KLUDGE:
When you've done something ugly say so and explain how you would do it differently next time
if you had more time.
• :TRICKY:
Tells somebody that the following code is very tricky so don't go changing it without thinking.
• :WARNING:
Beware of something.
• :COMPILER:
Sometimes you need to work around a compiler problem. Document it. The problem may go
away eventually.
• :ATTRIBUTE: value
The general form of an attribute embedded in a comment. You can make up your own attributes
and they'll be extracted.
Gotcha Formatting
• Make the gotcha keyword the first symbol in the comment.
• Comments may consist of multiple lines, but the first line should be a self-containing,
meaningful summary.
• The writer's name and the date of the remark should be part of the comment. This information is
in the source repository, but it can take a quite a while to find out when and by whom it was
added. Often gotchas stick around longer than they should. Embedding date information allows
other programmer to make this decision. Embedding who information lets us know who to ask.
Example
// :TODO: tmh 960810: possible performance problem
// We should really use a hash table here but for now we'll
// use a linear search.
See Also
See Interface and Implementation Documentation for more details on how documentation should be
laid out.
Class Users
Class users need class interface information which when structured correctly can be extracted directly
from a header file. When filling out the header comment blocks for a class, only include information
needed by programmers who use the class. Don't delve into algorithm implementation details unless the
details are needed by a user of the class. Consider comments in a header file a man page in waiting.
Class Implementors
Class implementors require in-depth knowledge of how a class is implemented. This comment type is
found in the source file(s) implementing a class. Don't worry about interface issues. Header comment
blocks in a source file should cover algorithm issues and other design decisions. Comment blocks
within a method's implementation should explain even more.
Directory Documentation
Every directory should have a README file that covers:
• the purpose of the directory and what it contains
• a one line comment on each file. A comment can usually be extracted from the NAME attribute
of the file header.
• cover build and install directions
• direct people to related resources:
• directories of source
• online documentation
• paper documentation
• design documentation
• anything else that might help someone
Consider a new person coming in 6 months after every original person on a project has gone. That lone
scared explorer should be able to piece together a picture of the whole project by traversing a source
directory tree and reading README files, Makefiles, and source file headers.
Example
#ifndef XX_h
#define XX_h
// SYSTEM INCLUDES
//
#include // standard IO interface
#include // HASA string interface
#include // USES auto_ptr
Notice how just by reading the include directives the code is starting to tell you a story of why and how
it was built.
Block Comments
Use comments on starting and ending a Block:
{
// Block1 (meaningful comment about Block1)
... some code
{
// Block2 (meaningful comment about Block2)
... some code
} // End Block2
} // End Block1
This may make block matching much easier to spot when you don't have an intelligent editor.
Complexity Management
Layering
Layering is the primary technique for reducing complexity in a system. A system should be divided
into layers. Layers should communicate between adjacent layers using well defined interfaces. When a
layer uses a non-adjacent layer then a layering violation has occurred.
A layering violation simply means we have dependency between layers that is not controlled by a well
defined interface. When one of the layers changes code could break. We don't want code to break so we
want layers to work only with other adjacent layers.
Sometimes we need to jump layers for performance reasons. This is fine, but we should know we are
doing it and document appropriately.
The GUI folks could instantiate each object and call the Jump method of each object. The Jump method
of each object contains the implementation of jumping behavior for that type of object. Obviously frogs
and beans jump differently even though both can jump.
Unfortunately the owner of Bean didn't like the word Jump so they changed the method name to Leap.
This broke the code in the GUI and one whole week was lost.
Then someone wanted to see a horse jump so a Horse class was added:
class Horse
{
public:
void Jump();
}
The GUI people had to change their code again to add Horse.
Then someone updated Horse so that its Jump behavior was slightly different. Unfortunately this
caused a total recompile of the GUI code and they were pissed.
Someone got the bright idea of trying to remove all the above dependencies using abstract base classes.
They made one base class that specified an interface for jumping things:
class Jumpable
{
public:
virtual void Jump() = 0;
}
Jumpable is a base class because other classes need to derive from it so they can get Jumpable's
interface. It's an abstract base class because one or more of its methods has the = 0 notation which
means the method is a pure virtual method. Pure virtual methods must be implemented by derived
classes. The compiler checks.
Not all methods in an ABC must be pure virtual, some may have an implementation. This is especially
true when creating a base class encapsulating a process common to a lot of objects. For example,
devices that must be opened, diagnostics run, booted, executed, and then closed on a certain event may
create an ABC called Device that has a method called LifeCycle which calls all other methods in turn
thus running through all phases of a device's life. Each device phase would have a pure virtual method
in the base class requiring implementation by more specific devices. This way the process of using a
device is made common but the specifics of a device are hidden behind a common interface.
Back to Jumpable. All the classes were changed to derive from Jumpable:
class Frog : public Jumpable
{
public:
virtual void Jump() { ... }
}
etc ...
We see an immediate benefit: we know all classes derived from Jumpable must have a Jump method.
No one can go changing the name to Leap without the compiler complaining. One dependency broken.
Another benefit is that we can pass Jumpable objects to the GUI, not specific objects like Horse or
Frog:
class Gui
{
public:
void MakeJump(Jumpable*);
}
Gui gui;
Frog* pFrog= new Frog;
gui.MakeJump(pFrog);
Notice Gui doesn't even know it's making a frog jump, it just has a jumpable thing, that's all it cares
about. When Gui calls the Jump method it will get the implementation for Frog's Jump method.
Another dependency down. Gui doesn't have to know what kind of objects are jumping.
We also removed the recompile dependency. Because Gui doesn't contain any Frog objects it will not
be recompiled when Frog changes.
Downside
Wow! Great stuff! Yes but there are a few downsides:
The idea is users of a class should be able to count on similar behavior from all classes that derive from
a base class. No special code should be necessary to qualify an object before using it. If you think about
it violating LSP is also violating the Open/Closed principle because the code would have to be
modified every time a derived class was added. It's also related to dependency management using
abstract base classes.
For example, if the Jump method of a Frog object implementing the Jumpable interface actually makes
a call and orders pizza we can say its implementation is not in the spirit of Jump and probably all other
objects implementing Jump. Before calling a Jump method a programmer would now have to check for
the Frog type so it wouldn't screw up the system. We don't want this in programs. We want to use base
classes and feel comfortable we will get consistent behaviour.
LSP is a very restrictive idea. It constrains implementors quite a bit. In general people support LSP and
have LSP as a goal.
Open/Closed Principle
The Open/Closed principle states a class must be open and closed where:
• open means a class has the ability to be extended.
• closed means a class is closed for modifications other than extension. The idea is once a class
has been approved for use having gone through code reviews, unit tests, and other qualifying
procedures, you don't want to change the class very much, just extend it.
The Open/Closed principle is a pitch for stability. A system is extended by adding new code not by
changing already working code. Programmers often don't feel comfortable changing old code because
it works! This principle just gives you an academic sounding justification for your fears :-)
In practice the Open/Closed principle simply means making good use of our old friends abstraction and
polymorphism. Abstraction to factor out common processes and ideas. Inheritance to create an
interface that must be adhered to by derived classes. In C++ we are talking about using abstract base
classes . A lot.
Register/Dispatch Idiom
Another strategy for reducing dependencies in a system is the Register/Dispatch Idiom (RDI). RDI
treats large grained occurrences in a system as events. Events are identified by some unique identifier.
Objects in the system register with a dispatch system for events or classes of events it is interested in.
Objects that are event sources send events into the dispatch system so the dispatch system can route
events to consumers.
RDI separates producers and consumers on a distributed scale. Event producers and consumers don't
have to know about each other at all. Consumers can drop out of the event stream by deregistering for
events. New consumers can register for events at anytime. Event producers can drop out with no ill
effect to event consumers, the consumer just won't get any more events. It is a good idea for producers
to have an "I'm going down event" so consumers can react intelligently.
Logically the dispatch system is a central entity. The implementation however can be quite different.
For a highly distributed system a truly centralized event dispatcher would be a performance bottleneck
and a single point of failure. Think of event dispatchers as being a lot of different processes cast about
on various machines for redundancy purposes. Event processors communicate amongst each other to
distribute knowledge about event consumers and producers. Much like a routing protocol distributes
routing information to its peers.
RDI works equally well in the small, in processes and single workstations. Parts of the system can
register as event consumers and event producers making for a very flexible system. Complex decisions
in a system are expressed as event registrations and deregistrations. No further level of cooperation
required.
More expressive event filters can also be used. The above proposal filters events on some unique ID.
Often you want events filtered on more complex criteria, much like a database query. For this to work
the system has to understand all data formats. This is easy if you use a common format like attribute
value pairs. Otherwise each filter needs code understanding packet formats. Compiling in filter code to
each dispatcher is one approach. Creating a downloadable generic stack based filter language has been
used with success on other projects, being both simple and efficient.
Delegation
Delegation is the idea of a method using another object's method to do the real work. In some sense the
top layer method is a front for the other method. Delegation is a form of dependency breaking. The top
layer method never has to change while it's implementation can change at will.
Delegation is an alternative to using inheritance for implementation purposes. One can use inheritance
to define an interface and delegation to implement the interface.
Some people feel delegation is a more robust form of OO than using implementation inheritance.
Delegation encourages the formation of abstract class interfaces and HASA relationships. Both of
which encourage reuse and dependency breaking.
Example
class TestTaker
{
public:
void WriteDownAnswer() { mPaidTestTaker.WriteDownAnswer(); }
private:
PaidTestTaker mPaidTestTaker;
}
In this example a test taker delegates actually answering the question to a paid test taker. Not ethical
but a definite example of delegation!
Justification
The purpose of this law is to break dependencies so implementations can change without breaking
code. If an object wishes to remove one of its contained objects it won't be able to do so because some
other object is using it. If instead the service was through an interface the object could change its
implementation anytime without ill effect.
Caveat
As for most laws the Law of Demeter should be ignored in certain cases. If you have a really high level
object that contains a lot of subobjects, like a car contains thousands of parts, it can get absurd to
created a method in car for every access to a subobject.
Example
class SunWorkstation
{
public:
void UpVolume(int amount) { mSound.Up(amount); }
SoundCard mSound;
private:
GraphicsCard mGraphics;
}
SunWorksation sun;
Do : sun.UpVolume(1);
Don't: sun.mSound.Up(1);
Design by Contract
The idea of design by contract is strongly related to LSP . A contract is a formal statement of what to
expect from another party. In this case the contract is between pieces of code. An object and/or method
states that it does X and you are supposed to believe it. For example, when you ask an object for its
volume that's what you should get. And because volume is a verifiable attribute of a thing you could
run a series of checks to verify volume is correct, that is, it satisfies its contract.
The contract is enforced in languages like Eiffel by pre and post condition statements that are actually
part of the language. In other languages a bit of faith is needed.
Design by contract when coupled with language based verification mechanisms is a very powerful idea.
It makes programming more like assembling spec'd parts.
section is some name that identifies why the code is chunked together. The class name and section
name are separated by '_'.
Class Layout
A common class layout is critical from a code comprehension point of view and for automatically
generating documentation. C++ programmers, through a new set of tools, can enjoy the same level
generated documentation Java programmers take for granted.
Template
Please use the following template when creating a new class.
/** A one line description of the class.
*
* #include "XX.h" <BR>
* -llib
*
* A longer description.
*
* @see something
*/
#ifndef XX_h
#define XX_h
// SYSTEM INCLUDES
//
// PROJECT INCLUDES
//
// LOCAL INCLUDES
//
// FORWARD REFERENCES
//
class XX
{
public:
// LIFECYCLE
/** Destructor.
*/
~XX(void);
// OPERATORS
// OPERATIONS
// ACCESS
// INQUIRY
protected:
private:
};
// INLINE METHODS
//
// EXTERNAL REFERENCES
//
#endif // _XX_h_
LIFECYCLE
The life cycle section is for methods that control the life cycle of an object. Typically these methods
include constructors, destructors, and state machine methods.
OPERATORS
Place all operators in this section.
OPERATIONS
Place the bulk of a class's non access and inquiry method methods here. A programmer will look here
for the meat of a class's interface.
ACCESS
Place attribute accessors here.
INQUIRY
These are the Is* methods. Whenever you have a question to ask about an object it can be asked via in
Is method. For example: IsOpen() will indicate if the object is open. A good strategy is instead of
making a lot of access methods you can turn them around to be questions about the object thus
reducing the exposure of internal structure. Without the IsOpen() method we might have had to do: if
(STATE_OPEN == State()) which is much uglier.
What should go in public/protected/private?
Public Section
Only put an object's interface in the public section. DO NOT expose any private data items in the
public section. At least encapsulate access via access methods. Ideally your method interface should
make most access methods unnecessary. Do not put data in the public interface.
All Protected
Some say there should be no private section and everything not in the public section should go in the
protected section. After all, we should allow all our children to change anything they wish.
All Private
Another camp says by making the public interface virtual any derived class can change behavior
without mucking with internals.
Wishy Washy
Rationally decide where elements should go and put them there. Not very helpful.
XX::XX()
{
}// XX
XX::XX(const XX&)
{
}// XX
XX::~XX()
{
}// ~XX
//============================= OPERATORS ====================================
XX&
XX::operator=(const XX&);
{
return *this;
}// =
#endif
#endif
1. Replace filename with the name of the file being guarded. This should usually be the name of
class contained in the file. Use the exact class name. Some standards say use all upper case.
This is a mistake because someone could actually name a class the same as yours but using all
upper letters. If the files end up be included together one file will prevent the other from being
included and you will be one very confused puppy. It has happened!
2. Most standards put a leading _ and trailing _. This is no longer valid as the C++ standard
reserves leading _ to compiler writers.
3. When the include file is not for a class then the file name should be used as the guard name.
4. Compilers differ on how comments are handled on preprocessor directives. Historically many
compilers have not accepted comments on preprocessor directives.
5. Historically many compilers require a new line after last endif.
Required Methods for a Class
To be good citizens almost all classes should implement the following methods. If you don't have to
define and implement any of the "required" methods they should still be represented in your class
definition as comments.
• Default Constructor
If your class needs a constructor, make sure to provide one. You need one if during the
operation of the class it creates something or does something that needs to be undone when the
object dies. This includes creating memory, opening file descriptors, opening transactions etc.
If the default constructor is sufficient add a comment indicating that the compiler-generated
version will be used.
If your default constructor has one or more optional arguments, add a comment indicating that it
still functions as the default constructor.
• Virtual Destructor
If your class is intended to be derived from by other classes then make the destructor virtual.
• Copy Constructor
If your class is copyable, either define a copy constructor and assignment operator or add a
comment indicating that the compiler-generated versions will be used.
If your class objects should not be copied, make the copy constructor and assignment operator
private and don't define bodies for them. If you don't know whether the class objects should be
copyable, then assume not unless and until the copy operations are needed.
• Assignment Operator
If your class is assignable, either define a assignment operator or add a comment indicating that
the compiler-generated versions will be used.
If your objects should not be assigned, make the assignment operator private and don't define
bodies for them. If you don't know whether the class objects should be assignable, then assume
not.
Justification
• Virtual destructors ensure objects will be completely destructed regardless of inheritance depth.
You don't have to use a virtual destructor when:
• You don't expect a class to have descendants.
• The overhead of virtualness would be too much.
• An object must have a certain data layout and size.
• A default constructor allows an object to be used in an array.
• The copy constructor and assignment operator ensure an object is always properly constructed.
Method Layout
The approach used is to place a comment block before each method that can be extracted by a tool and
be made part of the class documentation. Here we'll use Doxygen which supports the Javadoc format.
See the Doxygen documentation for a list of attributes supported by the document generator.
Method Header
Every parameter should be documented. Every return code should be documented. All exceptions
should be documented. Use complete sentences when describing attributes. Make sure to think about
what other resources developers may need and encode them in with the @see attributes.
/** Assignment operator.
*
*
* @param val The value to assign to this object.
* @exception LibaryException The explanation for the exception.
* @return A reference to this object.
*/
XX& operator=(XX& val);
Additional Sections
In addition to the standard attribute set, the following sections can be included in the documentation:
1. PRECONDITION
Document what must have happened for the object to be in a state where the method can be
called.
2. WARNING
Document anything unusual users should know about this method.
3. LOCK REQUIRED
Some methods require a semaphore be acquired before using the method. When this is the case
use lock required and specify the name of the lock.
4. EXAMPLES
Include exampes of how to use a method. A picture says a 1000 words, a good example answers
a 1000 questions.
For example:
/** Copy one string to another.
*
* PRECONDITION
* REQUIRE(from != 0)
* REQUIRE(to != 0)
*
* WARNING
* strcpy(somebuf, "test")
*
* * @param from The string to copy. * @param to The buffer to copy the string to.
* * @return void */ void strcpy(const char* from, char* to);
Implementing Accessors
There are three major idioms for creating accessors.
Get/Set
class X
{
public:
int GetAge() const { return mAge; }
void SetAge(int age) { mAge= age; }
private:
int mAge;
}
Similar to Get/Set but cleaner. Use this approach when not using the Attributes as Objects approach.
Attributes as Objects
class X
{
public:
int Age() const { return mAge; }
int& rAge() { return mAge; }
The above two attribute examples shows the strength and weakness of the Attributes as Objects
approach.
When using an int type, which is not a real object, the int is set directly because rAge() returns a
reference. The object can do no checking of the value or do any representation reformatting. For many
simple attributes, however, these are not horrible restrictions. A way around this problem is to use a
class wrapper around base types like int.
When an object is returned as reference its = operator is invoked to complete the assignment. For
example:
X x;
x.rName()= "test";
This approach is also more consistent with the object philosophy: the object should do it. An object's =
operator can do all the checks for the assignment and it's done once in one place, in the object, where it
belongs. It's also clean from a name perspective.
When possible use this approach to attribute access.
Justification
• When using C++'s ability to initialize variables in the constructor it's difficult with multiple
constructors and/or multiple attributes to make sure all attributes are initialized. When an
attribute is added or changed almost invariably we'll miss changing a constructor.
It's better to define one method, Init(), that initializes all possible attributes. Init() should be
called first from every constructor.
• The Init() idiom cannot be used in two cases where initialization from a constructor is required:
• constructing a member object
• initializing a member attribute that is a reference
Example
class Test
{
public:
Test()
{
Init(); // Call to common object initializer
}
Test(int val)
{
Init(); // Call to common object initializer
mVal= val;
}
private:
int mVal;
String* mpName;
void Init()
{
mVal = 0;
mpName= 0;
}
}
class Test
{
public:
Test(int val = 0, String* name = 0)
: mVal(val), mpName(name) {}
private:
int mVal;
String* mpName;
};
Justification
• More problems than you can believe are eventually traced back to a pointer or variable left
uninitialized. C++ tends to encourage this by spreading initialization to each constructor. See
Init Idiom for Initializing Objects .
Minimize Inlines
Minimize inlining in declarations or inlining in general. As soon as you put your C++ code in a shared
library which you want to maintain compatibility with in the future, inlined code is a major pain in the
butt. It's not worth it, for most cases.
Think About What Work to do in Constructors
Should you do work that can fail in constructors? If you have a compiler that does not support
exceptions (or thread safe exceptions if it matters to you) then the answer is definitely no. Go directly
to Do Work in Open. If your compiler supports exception then go to Do Work in Constructor. There
are still reasons to use an Open method even with exceptions.
Do Work in Constructor
With exceptions work done in the constructor can signal failure so it is fine to perform real work in the
constructor. This is the guru endorced approach as a matter of fact. But there are reasons to still use an
open style approach.
The constructor code must still be very careful not to leak resources in the constructor. It's possible to
throw an exception and not destruct objects allocated in the constructor.
There is a pattern called Resource Acquisition as Initialization that says all initialization is performed
in the constructor and released in the destructor. The idea is that this is a safer approach because it
should reduce resource leaks.
Do Work in Open
Do not do any real work in an object's constructor. Inside a constructor initialize variables only and/or
do only actions that can't fail.
Create an Open() method for an object which completes construction. Open() should be called after
object instantiation.
Example
class Device
{
public:
Device() { /* initialize and other stuff */ }
int Open() { return FAIL; }
};
Device dev;
if (FAIL == dev.Open()) exit(1);
Justification
• Very few people will have the same intuition as you about what a particular operator will do.
Short Methods
• Methods should limit themselves to a single page of code.
Justification
• The idea is that the each method represents a technique for achieving a single objective.
• Most arguments of inefficiency turn out to be false in the long run.
• True function calls are slower than not, but there needs to a thought out decision (see premature
optimization).
In a Source file Indicate if a Method is Static or Virtual
In a source file you can't tell a method is static or virtual because this information is in the header file.
Knowing this information in a source file is useful and can be communicated using comments:
/*virtual*/ void
Class::method()
{
}
/*static*/ void
Class::method()
{
}
I've only seen this format once in source code, but it is interesting enough that I thought I would
include here for your consideration. Notice how the method name sits alone on its own line. This looks
more like C code and looks very clean for some reason.
Process
Use a Design Notation and Process
Programmers need to have a common language for talking about coding, designs, and the software
process in general. This is critical to project success.
Any project brings together people of widely varying skills, knowledge, and experience. Even if
everyone on a project is a genius you will still fail because people will endlessly talk past each other
because there is no common language and processes binding the project together. All you'll get is
massive fights, burnout, and little progress. If you send your group to training they may not come back
seasoned experts but at least your group will all be on the same page; a team.
There are many popular methodologies out there. The point is to do some research, pick a method, train
your people on it, and use it. Take a look at the top of this page for links to various methodologies.
You may find an Agile methodology to your liking. For more information see
http://www.possibility.com/epowiki/Wiki.jsp?page=agile.
You may find the CRC (class responsibility cards) approach to teasing out a design useful. Many
others have. It is an informal approach encouraging team cooperation and focusing on objects doing
things rather than objects having attributes. There's even a whole book on it: Using CRC Cards by
Nancy M. Wilkinson.
Requirements Capture
Use cases attempt to capture the requirements for a system in an understandable form. The idea is by
running through a set of use case we can verify that the system is doing what it should be doing.
Have as many use cases as needed to describe what a system needs to accomplish.
The Process
• Start by understanding the system you are trying to build.
• Create a set of use cases describing how the system is to be used by all its different audiences.
• Create a class and object model for the system.
• Run through all the use cases to make sure your model can handle all the cases. Update your
model and create new use cases as necessary.
Using Stories
A user story consists of just a phrase or a few sentences written down, announcing something the user
wants to do. The user story is not used as a requirements specification, but as a marker for a future
conversation.
• User stories are used as tokens in the planning process after assessment of business value and
risk. The customer prioritizes the stories and schedules them for implementation.
• User Story is a story about how the system is supposed to solve a problem. Each UserStory
represents a chunk of functionality that is coherent in some way to the customer.
• User Story is a token that stands in for a conversation whose purpose is roughly equivalent to
that of a UseCase. The content of a UserStory is often an abbreviated version of the main
success scenario of a UseCase, but it doesn't have to be.
• User Story is a marker that reminds the customer and developers what to talk about, and also
inspires them in exploring some particular area of business value. A UserStory, then, contains
whatever the customer thinks is necessary to jog the memories and inspirations of those who
will later explore the story. Expertise in the business area will help the customer decide what are
the essential "jogs" to record in the initial UserStory marker.
• Developers should be able to implement a story in 2-4 weeks. The iteration length depends on
you project.
A Story Is
• Testable -You can write automatic tests to detect the presence of the story.
• Progress - The customers side of the team is willing to accept the story as a sign of progress
toward their larger goal.
• Bite-sized - The story should be completable within the iteration.
• Estimatable - The technical side of the team must be able to guess how much of the team's time
the story will require to get working.
Code Reviews
If you can make a formal code review work then my hat is off to you. Code reviews can be very useful.
Unfortunately they often degrade into nit picking sessions and endless arguments about silly things.
They also tend to take a lot of people's time for a questionable payback.
My god he's questioning code reviews, he's not an engineer!
Not really, it's the form of code reviews and how they fit into normally late chaotic projects is what is
being questioned.
First, code reviews are way too late to do much of anything useful. What needs reviewing are
requirements and design. This is where you will get more bang for the buck.
Get all relevant people in a room. Lock them in. Go over the class design and requirements until the
former is good and the latter is being met. Having all the relevant people in the room makes this
process a deep fruitful one as questions can be immediately answered and issues immediately explored.
Usually only a couple of such meetings are necessary.
If the above process is done well coding will take care of itself. If you find problems in the code review
the best you can usually do is a rewrite after someone has sunk a ton of time and effort into making the
code "work."
You will still want to do a code review, just do it offline. Have a couple people you trust read the code
in question and simply make comments to the programmer. Then the programmer and reviewers can
discuss issues and work them out. Email and quick pointed discussions work well. This approach meets
the goals and doesn't take the time of 6 people to do it.
For more information on code reviews please take a look at here. You'll find a lot of information on
justifying code reviews if you are having troubles instituting them and lots of suggestions on how to
conduct them.
Justification
• The reasoning is your source control system already keeps all this information. There is no
reason to clutter up source files with duplicate information that:
• makes the files larger
• makes doing diffs difficult as non source code lines change
• makes the entry into the file dozens of lines lower in the file which makes a search or
jump necessary for each file
• is easily available from the source code control system and does not need embedding in
the file
• When files must be sent to other organizations the comments may contain internal details that
should not be exposed to outsiders.
Honor Responsibilities
Responsibility for software modules is scoped. Modules are either the responsibility of a particular
person or are common. Honor this division of responsibility. Don't go changing things that aren't your
responsibility to change. Only mistakes and hard feelings will result.
Face it, if you don't own a piece of code you can't possibly be in a position to change it. There's too
much context. Assumptions seemingly reasonable to you may be totally wrong. If you need a change
simply ask the responsible person to change it. Or ask them if it is OK to make such-n-such a change. If
they say OK then go ahead, otherwise holster your editor.
Every rule has exceptions. If it's 3 in the morning and you need to make a change to make a deliverable
then you have to do it. If someone is on vacation and no one has been assigned their module then you
have to do it. If you make changes in other people's code try and use the same style they have adopted.
Programmers need to mark with comments code that is particularly sensitive to change. If code in one
area requires changes to code in an another area then say so. If changing data formats will cause
conflicts with persistent stores or remote message sending then say so. If you are trying to minimize
memory usage or achieve some other end then say so. Not everyone is as brilliant as you.
The worst sin is to flit through the system changing bits of code to match your coding style. If someone
isn't coding to the standards then ask them or ask your manager to ask them to code to the standards.
Use common courtesy.
Code with common responsibility should be treated with care. Resist making radical changes as the
conflicts will be hard to resolve. Put comments in the file on how the file should be extended so
everyone will follow the same rules. Try and use a common structure in all common files so people
don't have to guess on where to find things and how to make changes. Checkin changes as soon as
possible so conflicts don't build up.
As an aside, module responsibilities must also be assigned for bug tracking purposes.
Process Automation
It's a sad fact of human nature that if you don't measure it or check for it: it won't happen. The
implication is you must automate as much of the development process as possible and provide direct
feedback to developers on specific issues that they can fix.
Process automation also frees up developers to do real work because they don't have to babysit builds
and other project time sinks.
Documentation Extraction
Related to this principle is the need to automatically extract documentation from the source code and
make it available on line for everyone to use. If you don't do this documentation will be seen as
generally useless and developers won't put as much effort into it. Making the documentation visible
encourages people to do a better job.
Tools Agreement
The reality of different tool preferences is something to deal with explicitly and openly. Tools include
IDEs, languages, editors, make program, source code control, bug system, debuggers, test framework,
etc. Some tool decisions by their nature must be project wide, other decisions can be customized per
developer.
A split might also be done by who is performing the build. For example, an IDE should be able to used
in local builds, but the make program would be used for nightly and release builds.
Certain things are easy/trivial/useful with one tool, but hard/complicated/stupid with another tool.
Unstated tool assumptions can be the source of a lot of confusion. "Get a better editor" is not always a
workable response, though sometimes that's all there is to it!
Non-Blocking Scheduling
Schedules are lies. Schedules suck. Yes, yes, yes. But we still need them.
The most effective scheduling rule i've used is to schedule so as to unblock others. The idea is to
complete the portions of a feature that will unblock those dependent on you. This way development
moves along smoothly because more lines of development can be active at a time. For example, instead
of implementing the entire database, implement the simple interface and stub it out. People can work
for a very long time this way using that portion of the feature that caused others not to block. Plus it's a
form of rapid prototyping because you get immediate feedback on these parts. Don't worry about the
quality of implementation because it doesn't matter yet.
Using Personas
Personas are a powerful design tool, especially when combined with responsibility driven design.
Cooper's personas are:.
simply pretend users of the system you're building. You describe
them, in a surprising amount of detail, and then design your
system for them.
I have a standard set of personas that i consider when creating a design/architecture that don't seem to
be common. When you write code their are a lot of personas looking over your shoulder:
1. other programmers using the code
2. maintenance
3. extension
4. documentation group
5. training group
6. code review
7. test and validation
8. manufacturing
9. field support
10.first and second line technical support
11.live debugging
12.post crash debugging
13.build system (documentation generation and automatic testing)
14.unit testing
15.system testing
16.source code control
17.code readers
18.legal
You are much more careful and more thorough when you really thing about all the personas, all the
different people and all their different roles and purposes.
• Traditional Unix policy of placing the initial brace on the same line as the keyword and the
trailing brace inline on its own line with the keyword:
if (condition) { while (condition) {
... ...
} }
Justification
• Another religious issue of great debate solved by compromise. Either form is acceptable, many
people, however, find the first form more pleasant. Why is the topic of many psychological
studies.
There are more reasons than psychological for preferring the first style. If you use an editor
(such as vi) that supports brace matching, the first is a much better style. Why? Let's say you
have a large block of code and want to know where the block ends. You move to the first brace
hit a key and the editor finds the matching brace. Example:
if (very_long_condition && second_very_long_condition)
{
...
}
else if (...)
{
..
}
To move from block to block you just need to use cursor down and your brace matching key.
No need to move to the end of the line to match a brace then jerk back and forth.
Justification
It ensures that when someone adds a line of code later there are already braces and they don't forget. It
provides a more consistent look. This doesn't affect execution speed. It's easy to do.
Justification
It provides safety when adding new lines while maintainng a compact readable form.
} // if valid
else
{
} // not valid
} // end forever
Indentation/Tabs/Space Policy
• Indent using 3, 4, or 8 spaces for each level.
• Do not use tabs, use spaces. Most editors can substitute spaces for tabs.
• Tabs should be fixed at 8 spaces. Don't set tabs to a different spacing, uses spaces instead.
• Indent as much as needed, but no more. There are no arbitrary rules as to the maximum
indenting level. If the indenting level is more than 4 or 5 levels you may think about factoring
out code.
Justification
• Tabs aren't used because 8 space indentation severely limits the number of indentation levels
one can have. The argument that if this is a problem you have too many indentation levels has
some force, but real code can often be three or more levels deep. Changing a tab to be less than
8 spaces is a problem because that setting is usually local. When someone prints the source tabs
will be 8 characters and the code will look horrible. Same for people using other editors. Which
is why we use spaces...
• When people using different tab settings the code is impossible to read or print, which is why
spaces are preferable to tabs.
• Nobody can ever agree on the correct number of spaces, just be consistent. In general people
have found 3 or 4 spaces per indentation level workable.
• As much as people would like to limit the maximum indentation levels it never seems to work
in general. We'll trust that programmers will choose wisely how deep to nest code.
Example
void
func()
{
if (something bad)
{
if (another thing bad)
{
while (more input)
{
}
}
}
}
Justification
• Keywords are not functions. By putting parens next to keywords keywords and function names
are made to look alike.
Example
if (condition)
{
}
while (condition)
{
}
strcpy(s, s1);
return 1;
Justification
• Even though with big monitors we stretch windows wide our printers can only print so wide.
And we still need to print code.
• The wider the window the fewer windows we can have on a screen. More windows is better
than wider windows.
• We even view and print diff output correctly on all terminals and printers.
If you have else if statements then it is usually a good idea to always have an else block for finding
unhandled cases. Maybe put a log message in the else even if there is no corrective action taken.
Condition Format
Always put the constant on the left hand side of an equality/inequality comparison. For example:
if ( 6 == errorNum ) ...
One reason is that if you leave out one of the = signs, the compiler will find the error for you. A second
reason is that it puts the value you are looking for right up front where you can find it instead of buried
at the end of your expression. It takes a little time to get used to this format, but then it really gets
useful.
switch Formatting
• Falling through a case statement into the next case statement shall be permitted as long as a
comment is included.
• The default case should always be present and trigger an error if it should not be reached, yet is
reached.
• If you need to create variables put all the code in a block.
Example
switch (...)
{
case 1:
...
// FALL THROUGH
case 2:
{
int v;
...
}
break;
default:
}
for (...)
{
while (...)
{
...
if (disaster)
goto error;
}
}
...
error:
clean up the mess
When a goto is necessary the accompanying label should be alone on a line and to the left of the code
that follows. The goto should be commented (possibly in the block header) as to its utility and purpose.
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 usually undocumented reason.
The two main problems with continue are:
• It may bypass the test condition
• It may bypass the increment/decrement expression
Consider the following example where both problems occur:
while (TRUE)
{
...
// A lot of code
...
if (/* some condition */) {
continue;
}
...
// A lot of code
...
if ( i++ > STOP_VALUE) break;
}
Note: "A lot of code" is necessary in order that the problem cannot be caught easily by the
programmer.
From the above example, a further rule may be given: Mixing continue with break in the same loop is a
sure way to disaster.
?:
The trouble is people usually try and stuff too much code in between the ? and :. Here are a couple of
clarity rules to follow:
• Put the condition in parens so as to set it off from other code
• If possible, the actions for the test should be simple functions.
• Put the action for the then and else statement on a separate line unless it can be clearly put on
one line.
Example
(condition) ? funct1() : func2();
or
(condition)
? long statement
: another long statement;
One Statement Per Line
There should be only one statement per line unless the statements are very closely related.
The reasons are:
1. The code is easier to read. Use some white space too. Nothing better than to read code that is
one line after another with no white space or comments.
Do:
char** a= 0; // add doc
char* x= 0; // add doc
Justification
• Clarity.
• Similarly blocks of initialization of variables should be tabulated.
• The ‘&’ and ‘*’ tokens should be adjacent to the type, not the name.
Example
DWORD mDword
DWORD* mpDword
char* mpChar
char mChar
mDword = 0;
mpDword = NULL;
mpChar = NULL;
mChar = 0;
class PopcornPopper
{
public:
virtual void Pop(void);
static PopcornPopper* Singleton();
};
In .cpp file:
/*virtual*/ void
PopcornPopper::Pop(void)
{
}// Pop
/*static*/ PopcornPopper*
PopcornPopper::Singleton()
{
}// Singleton
A source file doesn't contain as much information as the header file because the virtual and static
keywords are dropped from method implementations. This is very useful information to have when
reading code, so we want to include it in the source file.
The condition causes the exception to be thrown when true. Msg1 and msg2 are local context. Reason
is the specific error code for the exception, if you think you need it.
Don't worry about running out of memory errors. Go ahead and allocate memory in your exception
code. If you are running out of memory that should be taken care of in the bad_alloc exception handler.
Exception Definition
There are as many definitions of exception as there are programmers:
There are those who say that exceptions should NOT be used to catch things like range errors. Here's
why:
Range errors fall into a class of problem that are called Programmer Errors. Programmer errors are
things that should NOT occur in a Bug Free Program. Ideally, range type problems are one of those
things that should be caught during development. It is for these problems that we have Design By
Contract.
You program defensively by putting lots of asserts in to make sure that your program is functioning as
expected. Then when you've committed a bug, like not checking user input properly, allowing an
invalid index into an array to be derived, the assert macro catches it, and the program does a gracful
crash.
Exceptions, on the other hand, should be used to catch problems that would arise even in a Bug Free
Program, i.e. Exceptional Circumstances. The most perfect program can still be afflicted by things like
shortage of memory and other resources, communications errors, and file problems. When one of these
things occurs, an exception should be thrown, and caught at a point where the program can either deal
with the problem, or close gracefully.
The real theory behind exceptions is to force the programmer to anticipate things the programmer has
no control over. Exceptions support the following kind of scenarios:
1. Logic error in the middle of a database transaction. An exception would allow the program a
chance to leave the database in a consistent state. A trashed database can be very expensive.
2. Logic error in the middle of a program that is using a resource ike a modem and the program
runs unattended. Exception handling would give the program a chance to hang up the modem
connection, rather than sit there with an connection until it's discovered the next day, running up
the phone bill.
3. Logic error in the middle of something like a word processor. Consider the user that has been
working for a couple of hours with unsaved edits, and your assert message pops up, and the last
two hours of work are pretty much lost - causing the user some misery. If you used exception
handling, you could at least give the user a chance to salvage the unsaved document in a
different file as part of the cleanup.
Design-by-Contract (DBC)
A design technique developed by Bertrand Meyer for producing "bug free" systems.
Design by Contract (DBC) views the relationship between a class and its clients as formal agreement,
expressing each party's rights and obligation. Rights and obligations are determined by a class'
specification. Correctness can only be determined in reference to an object's specification.
Specifications, in DBC, are expressed through assertions.
Design by Contract is a development approach where a specification is associated with every software
element. These specifications (or contracts) govern the interaction of the element with the rest of the
world. A contract takes form as a set of preconditions, postconditions, and invariants that are run as the
system executes. The contract is published in the comment block of each method.
Assertion
A boolean statement that should never be false and, therefore, will only be false because of a bug.
Typically assertions are checked only during debug and are not checked during production execution,
although they can be left in when performance and memory size are not issues.
Design by Contract uses three kinds of assertions: post-conditions, pre-conditions, and invariants. Pre-
conditions and post-conditions apply to operations. Invariants apply to the class.
Pre-condition
A statement of how we expect the world to be before we execute an operation. We might design a
pre-condition of the "square" operation as this >= 0. Such a pre-condition says that it is an error to
invoke "square" on a negative number.
Post-condition
A statement of what the world should look like after execution of an operation. For example, if we
define the operation "square" on a number, the post-condition would take the form result = this *this.
The post-condition is a useful way of saying what we do without saying how we do it- in other words,
of separating interface from implementation.
Invariant
An invariant is an assertion about a class. For instance, an Account class may have an invariant that
says that balance == sum(entries.amount()). The invariant is always true for all instances of the class.
"Always" means whenever the object is available for an operation to be invoked. During an operation
invariants may not be satisfied, but invariants must hold when an operation has finished.
Let's say an exception is thrown while deleting the database connection. Will the buffer be deleted? No.
Exceptions are basically non-local gotos with stack cleanup. The code for deleting the buffer will never
be executed creating a gaping resource leak.
Special care must be taken to catch exceptions which may occur during object destruction. Special care
must also be taken to fully destruct an object when it throws an exception.
Usign RAII can help prevent many of not most of these type of errors.
class Calendar
{
public:
boost::gregorian::date GetEventDate(void) const;
void SetEventDate(boost::gregorian::date dateOfTheEvent);
};
Clearly these are long names and are noisy and are pain to use. So you are tempted to put at the top of
you class file:
using namespace boost::gregorian; // make namespace global for this file
This would mean you could just use "date" instead of "boost::gregorian::date". That's nice. But you
can't do that. If you do you are making the decision for everyone who uses your class as well. They
may have a conflict, "date" is a very common name afterall.
So, don't use using in you header file, but you can use it in your source file. Because it's your source
file you can make the decision to use using to shorten up the names. This strategy preserves most of the
convenience while being a good citizen.
Create Shortcut Names
You can use using to create aliases or shortcuts, that is names that are more convenient to access than
long namespace names.
For example:
namespace alias = a::very::long::namespace;
class YourClass: public alias::TheirClass
{ ... }
Clearly this makes code both harder to read and easier to read. The code is harder to read because you
as a programmer have to compile the alias directive in your head and know what it means whenever it
is used in the code.
The code is easier to read because it is less complex. Complex namespaces are distracting and
confusing. Even with the additional cognitive load, using namespace alias make code a lot easier to
read.
Miscellaneous
This section contains some miscellaneous do's and don'ts.
• Don't use floating-point variables where discrete values are needed. Using a float for a loop
counter is a great way to shoot yourself in the foot. Always test floating-point numbers as <= or
>=, never use an exact comparison (== or !=).
• Compilers have bugs. Common trouble spots include structure assignment and bit fields. You
cannot generally predict which bugs a compiler has. You could write a program that avoids all
constructs that are known broken on all compilers. You won't be able to write anything useful,
you might still encounter bugs, and the compiler might get fixed in the meanwhile. Thus, you
should write ``around'' compiler bugs only when you are forced to use a particular buggy
compiler.
• Do not rely on automatic beautifiers. The main person who benefits from good program style is
the programmer him/herself, and especially in the early design of handwritten algorithms or
pseudo-code. Automatic beautifiers can only be applied to complete, syntactically correct
programs and hence are not available when the need for attention to white space and indentation
is greatest. Programmers can do a better job of making clear the complete visual layout of a
function or file, with the normal attention to detail of a careful programmer (in other words,
some of the visual layout is dictated by intent rather than syntax and beautifiers cannot read
minds). Sloppy programmers should learn to be careful programmers instead of relying on a
beautifier to make their code readable. Finally, since beautifiers are non-trivial programs that
must parse the source, a sophisticated beautifier is not worth the benefits gained by such a
program. Beautifiers are best for gross formatting of machine-generated code.
• 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 usually no. The solution is to
just not do it, an inverse Nike philosophy. 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) { ... }
• Modern compilers will put variables in registers automatically. Use the register sparingly to
indicate the variables that you think are most critical. In extreme cases, mark the 2-4 most
critical values as register and mark the rest as REGISTER. The latter can be #defined to register
on those machines with many registers.
Be Const Correct
C++ provides the const key word to allow passing as parameters objects that cannot change to indicate
when a method 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.
If you don't use const correctness from the start it can be nightmare to add it in later because it causes a
chain reaction of needing const everywhere. It's better to start being const correct from the start or you
probably won't be.
You can always cast aways constness when necessary, but it's better not to.
For more information see Const Correctness in the C++ FAQ.
This looks disturbingly different than the more common "const int A_GLOBAL_CONSTANT= 5"
syntax we are used to seeing. But, according to the standards, the qualifier modifies everything to its
left with the special exception where the qualifier precedes the first type and modifies just that type.
Since there is one generalized form that works for all occasions, it is more consistent to always put the
qualifier to the right of whatever is qualified.
Use Streams
Programmers transitioning from C to C++ find stream IO strange preferring the familiarity of good old
stdio. Printf and gang seem to be more convenient and are well understood.
Type Safety
Stdio is not type safe, which is one of the reasons you are using C++, right? Stream IO is type safe.
That's one good reason to use streams.
Standard Interface
When you want to dump an object to a stream there is a standard way of doing it: with the << operator.
This is not true of objects and stdio.
Interchangeablity of Streams
One of the more advanced reasons for using streams is that once an object can dump itself to a stream it
can dump itself to any stream. One stream may go to the screen, but another stream may be a serial port
or network connection. Good stuff.
Streams Got Better
Stream IO is not perfect. It is however a lot better than it used to be. Streams are now standardized,
acceptably efficient, more reliable, and now there's lots of documentation on how to use streams.
No Magic Numbers
A magic number is a bare naked number used in source code. It's magic because no-one has a clue
what it means including the author inside 3 months. For example:
if (22 == foo) { start_thermo_nuclear_war(); }
else if (19 == foo) { refund_lotso_money(); }
else if (16 == foo) { infinite_loop(); }
else { cry_cause_im_lost(); }
In the above example what do 22 and 19 mean? If there was a number change or the numbers were just
plain wrong how would you know?
Instead of magic numbers use a real name that means something. You can use #define or constants or
enums as names. Which one is a design choice. For example:
#define PRESIDENT_WENT_CRAZY (22)
const int WE_GOOFED= 19;
enum
{
THEY_DIDNT_PAY= 16
};
Now isn't that better? The const and enum options are preferable because when debugging the
debugger has enough information to display both the value and the label. The #define option just shows
up as a number in the debugger which is very inconvenient. The const option has the downside of
allocating memory. Only you know if this matters for your application.
A C++ Workaround
C++ allows static class variables. These variables are available anywhere and only the expected amount
of space is taken.
Example
class Variables
{
public:
static const int A_VARIABLE;
static const int B_VARIABLE;
static const int C_VARIABLE;
}
Macros
Don't Turn C++ into Pascal
Don't change syntax via macro substitution. It makes the program unintelligible to all but the
perpetrator.
Example
#define MAX(x,y) (((x) > (y) ? (x) : (y)) // Get the maximum
The macro above can be replaced for integers with the following inline function with no loss of
efficiency:
inline int
max(int x, int y)
{
return (x > y ? x : y);
}
Example
MAX(f(x),z++);
Example
#define ADD(x,y) x + y
must be written as
if (FAIL != f())
is better than
if (f())
even though FAIL may have the value 0 which C considers to be false. An explicit test will help you
out later when somebody decides that a failure return should be -1 instead of 0. Explicit comparison
should be used even if the comparison value will never change; e.g., if (!(bufsize % sizeof(int)))
should be written instead as if ((bufsize % sizeof(int)) == 0) to reflect the numeric (not boolean)
nature of the test. A frequent trouble spot is using strcmp to test for string equality, where the result
should never ever be defaulted. The preferred approach is to define a macro STREQ.
inline bool
StringEqual(char* a, char* b)
{
return strcmp(a, b) == 0;
}
Note, this is just an example, you should really use the standard library string type for doing the
comparison.
The non-zero test is often defaulted for predicates and other functions or expressions which meet the
following restrictions:
• Returns 0 for false, nothing else.
• Is named so that the meaning of (say) a true return is absolutely obvious. Call a predicate
IsValid(), not CheckValid().
or
Note, the standard defines the names true and false not TRUE and FALSE. The all caps versions are
used to not clash if the standard versions are available.
Even with these declarations, do not check a boolean value for equality with 1 (TRUE, YES, etc.);
instead test for inequality with 0 (FALSE, NO, etc.). Most functions are guaranteed to return 0 if false,
but only non-zero if true. Thus,
must be written
The ++ and -- operators count as assignment statements. So, for many purposes, do functions with side
effects. Using embedded assignment statements to improve run-time performance is also possible.
However, one should consider the tradeoff between increased speed and decreased maintainability that
results when embedded assignments are used in artificial places. For example,
a = b + c;
d = a + r;
d = (a = b + c) + r;
even though the latter may save one cycle. In the long run the time difference between the two will
decrease as the optimizer gains maturity, while the difference in ease of maintenance will increase as
the human memory of what's going on in the latter piece of code begins to fade.
Keep a Repository
Most companies have no idea what code they have. And most programmers still don't communicate
what they have done or ask for what currently exists. The solution is to keep a repository of what's
available.
In an ideal world a programmer could go to a web page, browse or search a list of packaged libraries,
taking what they need. If you can set up such a system where programmers voluntarily maintain such a
system, great. If you have a librarian in charge of detecting reusability, even better.
Another approach is to automatically generate a repository from the source code. This is done by using
common class, method, library, and subsystem headers that can double as man pages and repository
entries.
Using #if 0
The easiest way to do this is with an #if 0 block:
void
example()
{
great looking code
#if 0
lots of code
#endif
more code
}
You can't use /**/ style comments because comments can't contain comments and surely a large block
of your code will contain a comment, won't it?
Don't use #ifdef as someone can unknowingly trigger ifdefs from the compiler command line.
#if OBSOLETE
#if TEMP_DISABLED
Someone else might compile the code with turned-of debug info like:
cc -c lurker.cpp -DDEBUG=0
Alway use #if, if you have to use the preprocessor. This works fine, and does the right thing, even if
DEBUG is not defined at all (!)
#if DEBUG
temporary_debugger_break();
#endif
If you really need to test whether a symbol is defined or not, test it with the defined() construct, which
allows you to add more things later to the conditional without editing text that's already in the program:
#if !defined(USER_NAME)
#define USER_NAME "john smith"
#endif
#else
extern some_function();
#endif
1. It's bad magic to have space consuming code silently inserted through the innocent use of
header files.
2. It's not common practice to define variables in the header file so it will not occur to devellopers
to look for this when there are problems.
3. Consider defining the variable once in a .cpp file and use an extern statement to reference it.
4. Consider using a singleton for access to the data.
My initial response was prompted by a general unacceptance of C++ in the project and blaming C++
for all problems. Of course all the parts written in C and assembly had no problems :-) Embedded
systems shops tend to be hardware driven companies and tend not to know much about software
development, thus any new fangled concepts like OO and C++ are ridiculed without verbally accessible
reasons. Counter arguments like code that is fast and small and reusable don't make a dent. Examples
like improving the speed of a driver by inlining certain methods and not hacking the code to death
gently roll into the bit bucket.
Techniques
Of course C++ can be a disaster for an embedded system when used incorrectly, which of course is true
of any tool. Here's some ideas to use C++ safely in an embedded system:
• Get Some Training!
If people don't know C++ and OO then they will likely fail and blame their tools. A good
craftsperson doesn't blame their tools. Get training. Hire at least one experienced person as
guide/mentor.
• Be Careful Using Streams
The streams library is large and slow. You are better off making a "fake" streams library by
overloading the << operator. If you have a lot of memory then use streams, they are convenient
and useful.
• Be Careful Using Templates
Code using templates can suffer from extreme code bloat. This is pretty much a function of your
compiler as templates can be efficiently used when done correctly. Test your compiler for it
how handles templates. If it doesn't make a copy per file for each template then you are in
business. Templates have good time efficiency so they would be nice to use.
You can fix the template code bloat problem by using explicit instantiation. Actually, even if
the compiler generates one copy per source file. This, however, is often too much programmer
work to expect on a large project, so be careful. Many linkers are smart enough to strip away all
but one of the copies.
Another issue to consider is template complexity. Templates can be complex for those new to
C++. Bugs in templates are very hard to find and may overwhelm the patience of users.
• Exceptions Beware
Embedded applications are usually interrupt driven and multi-threaded. Test that exceptions are
thread safe. Many compilers support exceptions, but not thread safe exceptions. And you
probably don't want to call code in an interrupt that throws exceptions.
• Use Polymorphic Interfaces to Make Frameworks
When you think through your design and come up with good abstractions you will be shocked
at how little code and how little time it takes to implement new features.
• Make an OS Encapsulation Library
Don't use your embedded OSs features directly. Create a layer that encapsulates OS functions
and use those encapsulations. Most feature like tasks, interrupts, semaphores, message queues,
messages, etc. are common to all systems. With good encapsulations it's quite possible to have
the same code compile for Solaris, VxWorks, Windows, and other systems. It just takes a little
thought.
• ROM Beware
A lot of systems create a ROM and download code later over the network that is linked against
the ROM. Something to remember is linkers will try and include only code that is used. So your
ROM may not contain code that loaded code expects to be there. You need to include all
functions in your ROM.
• Multiple Interface Levels
Most embedded systems have a command line interface which usually requires C linkage, then
they may have an SNMP interface, and they may have some sort of other friendly interface.
Design this up front to be common across all code. It will make your life much easier. C
functions require access to global pointers so they can use objects. The singleton pattern makes
this easier.
Come up with common naming conventions. A decent one is:
• Make up a module abbreviation that can be prefixed to all calls. For example: log for the
logging module.
• Encode an action after the prefix. For example: logHelp which prints help for the
logging module.
• Require a certain set of functions for each sub system: For example:
• moduleHelp - prints help for the module
• modulePrint - prints the current state of the module
• moduleStart - start a module
• moduleStop - stop a module
• moduleSetDebug - set the debug level for a module. It's very nice to set debug
levels on a module by module basis.
• Debug and Error System First
Make your debug and error system first so everyone writing code will use it. It's very hard to
retrofit code with debug output and intelligent use of error codes. If you have some way to write
system assert errors to NVRAM, disk, or some other form of persistent storage so you can
recover it on the next reboot.
• Think About Memory
Think how you'll share memory buffers between ISR code and task level code. Think how fast
your default memory allocator is, it is probably slow. Think if your processor supports purify!
Think how you'll track memory corruption and leakage.
• Think About System Integrity
You need to design up front how you are going to handle watchdog functions and test that the
system is still running and not corrupted.
• Remember to Use Volatile
When using memory mapped I/O make sure that you declare the input port variables as volatile,
(some compilers do this automatically), since the value can change without notice, and the
optimizer could eliminate what looks like a redundant access to that variable. Not using volatile
leads to some very obscure bugs. If you suspect problems in this area take a look at the
generated code to make sure read-only assumptions are being made.
Sometimes the keyword volatile is ifdef'd out for portability reasons. Check that what you think
is volatile is really declared as volatile.