All code must >parse successfully. If it doesn't, try to use a construct
that the parser understands. Failing that, enhance the parser (or ask for
it to be enhanced) to support the missing language feature.
Use the >check command to help determine whether software follows
recommended guidelines. It supports many of the guidelines in this
document, and the >fix command can even interactively repair some
of them.
The existing software does not always follow every guideline. Sometimes,
there is a good reason to violate a guideline. In others, the effort that
would be needed to make the software conform is better spent elsewhere.
Some of the software was developed before C++11, so there are things that
it should still adopt (greater use of unique_ptr, for example).
Try to make it impossible for the reader to tell where code was added or changed.
- Begin each file with the following heading:
//================================================================================
//
// <FileName.ext>
//
// Copyright (C) 2013-2025 Greg Utas
//
// This file is part of the Robust Services Core (RSC).
//
// RSC is free software: you can redistribute it and/or modify it under the
// terms of the Lesser GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// RSC is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the Lesser GNU General Public License
// along with RSC. If not, see <http://www.gnu.org/licenses/>.
-
Use spaces instead of tabs.
-
Indent a multiple of 3 spaces.
-
Remove unnecessary interior spaces and semicolons. Remove trailing spaces.
-
Use
//comments instead of/*...*/. -
Add blank lines for readability, but do not use consecutive blank lines.
-
Limit lines to 80 characters in length. Break after
,:)and before(. A break at an operator may occur before or after, depending on which seems to read better. -
Break before
{and}unless everything in between also fits on the same line. -
Almost always use PascalCase. Use all uppercase and interior underscores only in low-level types and constants. Names that evoke Hungarian notation are an abomination.
-
Keep
*and&with the type instead of the variable (Type* tinstead ofType *t).
-
Insert an
#includeguard based on the file name (FileName.h andFILENAME_H_INCLUDED) immediately after the standard heading. -
Sort
#includedirectives alphabetically within the following groups:- header(s) that define base classes of classes defined in this header
- external headers (
#include <filename>) - innteral headers (
#include "filename.h")
-
Remove an
#includesolely associated with functions inherited from a base class. -
Remove an
#includeby forward-declaring a class that is only used with references or pointers. Use an explicit forward declaration instead of relying on this as a side effect of a friend declaration. -
Avoid
usingdeclarations and directives forstdsymbols and those of other namespaces. Prefix the namespace directly (std::<symbol>, for example). -
Initialize global data (static members) in the .cpp if possible.
Only use the preprocessor for one of the following purposes:
-
An
#includeguard. -
Conditional compilation (
#ifdef). The symbols are defined when the compiler is launched. Those in current use areOS_WINandOS_LINUXfor Windows and Linux. These specify the target platform and may only be used in platform-specific files, which are named *.win.cpp and *.linux.cpp.FIELD_LOADfor a production build (else assumed to be a lab build). May only be used in a .cpp that executes before the configuration file is read during system initialization; otherwise useElement::RunningInLab().WORDSIZE_32for a 32-bit CPU (else assumed to be 64-bit). May only be used in subs/ files.CT_COMPILERwhen running the>parsecommand. May only be used in subs/ files.
-
To
#definean imitation keyword that maps to an empty string. The only current examples areNO_OPandNO_FT.
-
Sort
#includedirectives alphabetically within the following groups:- header(s) that declare something that this .cpp defines
- header(s) that define direct base classes of classes defined in this .cpp
- external headers (
#include <filename>) - interal headers (
#include "filename.h")
-
Omit an
#includeorusingthat is already in the header. -
Put all of the code in the same namespace as the items declared in the header.
-
Implement functions in alphabetical order, after the constructor(s) and destructor.
-
Separate functions that belong to the same scope with a
//----... that is 80 characters long. -
Separate classes and functions that belong to different scopes (local to the .cpp) with a
//====... that is 80 characters long. -
Add a blank line after the
fn_namethat defines a function’s name forDebug::ftinvocations. -
Name constructors and destructors
"<class>.ctor"and"<class>.dtor"forDebug::ftinvocations. -
Fix compiler warnings through level 4.
-
Give each class its own .h and .cpp unless it is trivial, closely related to others, or private to an implementation.
-
A base class should be abstract. Its constructor should therefore be protected.
-
Tag a constructor
explicitif it can be invoked with one argument. -
Make each public function non-virtual, with a one-line invocation of a virtual function if necessary.
-
Make each virtual function private if possible, or protected if derived classes may need to invoke it.
-
Make a base class destructor
- virtual and public
- non-virtual and protected
- virtual and protected, to restrict deletion
-
If a destructor frees a resource, even automatically through a
unique_ptrmember, also define- a copy constructor:
Class(const Class& that); - a copy assignment operator:
Class& operator=(const Class& that);Here, create copies ofthat’s resources first, then releasethis’s existing resources, and finally assign the new ones.
- a copy constructor:
-
If a class allows copying, also define "move" functions. These release
this’s existing resources and then take over those owned bythat:- a move constructor:
Class(Class&& that); - a move assignment operator:
Class& operator=(Class&& that);
The implementations typically use
std::swap. - a move constructor:
-
Each of the above functions can be suffixed with
= deleteto prohibit its use, or= defaultto use the compiler-generated default. -
To prohibit stack allocation, delete the constructor and/or destructor.
-
To prohibit scalar heap allocation, delete
operator new. -
To prohibit vector heap allocation, delete
operator new[]. -
If a class only has static members, convert it to a namespace. If it is a class so that some members can be private, delete its constructor.
-
Use
overridewhen overriding a function defined in a base class. -
Within the same level of access control, sort overrides alphabetically, and place them after functions that are not overrides.
-
Make a function or argument
constwhen appropriate. -
Remove
inlineas a keyword. -
Avoid
friendwhere possible. -
Override
Displayif a class has data. -
Override
Patchexcept in a trivial leaf class. -
Avoid invoking virtual functions in the same class hierarchy within constructors and destructors.
-
Provide an implementation for a pure virtual function to highlight the bug of calling it too early during construction or too late during destruction.
-
If a class is large, consider using the PIMPL idiom to move its private members to the .cpp.
-
When only a subset of a class’s data should be write-protected, split it into a pair of collaborating classes that derive from
ProtectedandPersistent(orImmutableandPermanent). -
Static member data begins with an uppercase letter and ends with an underscore, which may be omitted if it is not returned by a "Get" function. Non-static member data begins with a lowercase letter and ends with an underscore, which may be omitted if it is public (in which case it should probably be in a
struct).
-
Use the initialization list for constructors. Initialize members in the order that the class declared them.
-
Use
()instead of(void)for an empty argument list. -
Name each argument. Use the same name in the interface and the implementation, and in the base class and its overrides.
-
Make the invocation of
Debug::ftthe first line in a function body, and follow it with a blank line. -
The
fn_namepassed toDebug::ftand other functions must accurately reflect the name of the invoking function. -
A simple "Get" function should not invoke
Debug::ftunless it is virtual. -
Left-align the types and variable names in a long declaration list.
-
Use
nullptrinstead ofNULL. -
Check for
nullptr, even when an argument is passed by reference. (A reference merely documents thatnullptris an invalid input.) -
After invoking
delete, set a pointer tonullptr. -
Use
unique_ptrto avoid the need fordelete. -
Use
unique_ptrso that a resource owned by a stack variable will be freed if an exception occurs. -
Declare loop variables inline (
for auto i =, for example). -
Declare variables of limited scope inline, as close to where they are used as is reasonable.
-
Use
autounless specifying the type definitely improves readability. -
Include
{...}in all non-trivialif,for, andwhilestatements. -
When there is an
else, use braces in both or neither clauses of theif. -
When there is no
else, use braces for the statement afterifunless it fits on the same line. -
Define string constants in a common location to support future localization.
-
To force indentation, even in the face of automated formatting, use
{...}between function pairs such asEnterBlockingOperationandExitBlockingOperationLockandUnlockMakePreemptableandMakeUnpreemptable
-
It is a serious bug for a function to cause an unexpected exception, so check arguments and results returned by other functions.
-
Throw an exception only when it is impossible to fail gracefully or the work being performed must be aborted. Prefer to generate a log (
Debug::SwLog) and return a failure value.
Some comments identify work items. They have the form //a, where a
is some character. The following are currently used:
//&is something inmain.cppthat might be enabled for a subset build//>is an internal constant that can be changed to raise or lower a limit//@is a useful breakpoint during development//Lis a Linux target enhancement//bis a basic call enhancement//cis aCodeToolsenhancement//dis a decoupling enhancement//eis an unclassified enhancement//pis a POTS enhancement//xis something to be removed (should not appear in a Release)//*is something to be fixed or implemented as soon as possible (should rarely appear in a Release)