GPS Toolkit Coding Standard
The purpose of this document is to define the C++ coding standards for code that will be submitted for inclusion in the GPS Toolkit. This document also contains
guidelines for the C++ coding. A standard is an item to which compliance is mandatory. Guidelines are suggested items.
1 Class Declarations
Standards
- Remember to use the inclusion guard
#ifndef ... #define ... #endif syntax in your header files so a class isn't included multiple times. The #define value should be the filename in all caps with the '.' replaced by an '_'.
#ifndef MOO_HPP
#define MOO_HPP
// class code goes here
#endif // MOO_HPP
- As much as possible, put one class in each header file. There are exceptions such as nested classes or thread classes.
- Use
const whenever you use a parameter that doesn't get modified by the function. This will ensure that the value of the parameter doesn't get changed, as well as ensure that others who use your code know it won't be modified.
Guidelines
- If you don't need them, disallow the copy constructor and assignment operator by making them private.
2 Naming Conventions
2.1 Class, Variable, and Function Names
Standards
- Whenever constant values are needed, use
const objects instead of #define statements. You may use all-caps with '_' characters in these constant definitions. Otherwise, stick to the accepted variable naming constraints.
const int MAX_NUMS = 23 ;
const int maxNums = 23 ;
- Start all variable and function names with a lowercase letter, then mix lower and upper case as appropriate.
getRxNumber(), tempObsData
- Do not start a name with '_'.
- Start
class and struct names with a capital letter.
- Use an enumeration,
enum, rather than defining arbitrary constants. Make sure to name and comment the enumeration values so their use can be understood. See the example header file for an example.
- Do not use "magic values" for loops. Instead, define a descriptive constant.
const int NUM_COWS = 2;
if(i < NUM_COWS)
- All accessor methods begin with the word "get", are named after the data member they return, and are
const as much as possible. The return a value, not a reference.
// returns number from the object
int getNumber() const;
- All modifying methods begin with the word "set", are named the same as the data member they modify (where applicable), and return a reference to the object they modify.
// assigns number to num in the object
Moo& setNumber(int num);
- Boolean accessors should start with a verb appropriate to their function. For instance "is", "has" and "can" each describe what those functions are testing. These should be
const wherever possible.
boolean isMoo() const;
boolean hasACow() const;
2.2 File Names
Standards
- C++ source files use the extension
.cpp.
- C++ header files use the extension
.hpp.
- C source files use the extension
.c.
- C header files use the extension
.h.
- Name source and header files containing a class
ClassName.cpp and ClassName.hpp.
- Submit a file called
Jamfile to build your code.
Guidelines
- Name the file with
main() the same as the executable name.
3 Code Layout
Standards
- Always use braces on
if...else, while and for blocks, even if only one line is in the block.
- Align the braces such that they are at the same scope as the statement that preceeds them and the code within is indented one level.
if (moo < MAX_MOO)
{
// code goes here
}
- In function declarations, put one parameter per line, making sure to indent parameters.
void moo (int aParam,
int anotherParam,
const string& aFinalParam);
- Like the above rule, do the same for lists of
if conditions or enumeration values for easier reading (specifically, to prevent line wrapping in the middle of names). Indent appropriately.
if ((someReallyLongVariableName == 1) &&
(anotherReallyLongVariableName == 2))
- When using pointers, place the '*' directly to the left of the object's name.
int *moo;
- When using references, place the '&' directly to the right of the type name.
int& moo;
- Use one statement per line except in short header code definitions. The maximum length of these functions is two lines (including the declaration and the code block). For example, this is an acceptable function declaration in a header file, but not in your source file:
inline Moo& setNumber(int num)
{ number = num; return *this; }
- If you have a large block of code, such as a long
if...else or case block, put a comment at the end noting the scope of the block that is enclosed. Do the same thing for #ifdef ... #endif blocks.
if (x < y)
{
// 15 +/- lines of code
} // if (x < y)
- Each level of indentation is three (3) spaces.
- Limit the line length to eighty (80) characters. Continue longer lines on the next line, indenting three (3) spaces.
4 Submission Requirements
Standards
- This is the macro expanded by Perforce into the file version information. Use it at the start of each file. (Remove the backslashes.)
$/Id: $
- Test the code before submission. Supply any test program(s) with the submitted code.
- Use the Standard Template Library wherever it is appropriate.
- Library code does not produce debugging text output. Any debugging text you need should be removed before submission.
- All data objects (where appropriate) should have a
STDOUT operator.
5 Code Comments
Standards
- Follow all Doxygen comment coding standards in the header files. Appendix A shows an example header file with Doxygen compliant comments.
- Comments at the start of a file are left justified.
- All comments in code, single line or block, should be indented with three (3) spaces.
// this is a correctly placed comment
int i;
- Use the Doxygen comments and the normal C/C++ comments properly. If you want to add a comment that will not be included in the documents, use the standard C/C++ comments ("=//=" and "=/* ... */=").
Guidelines
- Comments belong above the line(s) of code they describe. The only exception where you can use comments at the end of a line is when marking the end of a long code block (i.e.
if ... else) or in describing enum values as in the example header file.
6 Code Style
Standards
- No tabs are allowed. Expand them into spaces (guideline: 3 spaces).
- Declare namespaces being used in the scope of the class so that other classes/programs that use the class don't inherit them.
- Any code which is known to be broken or questionable, mark with a comment and
FIX.
// FIX this because it doesn't work when ...
- Functions which need to be defined in a header (i.e.
inline) but that are longer than two lines should be declared in the .cpp file ( or at the end of the .hpp file if there is no .cpp or it is a template function).
- Explicitly using
this-> is only necessary when the scope of a variable is unclear (and if this is the case, you should rename your variables to clear up the ambiguity). Otherwise, its usage is not necessary.
- Any virtual function must also be virtual in its parent/children classes. Any class with virtual methods must have a virtual destructor.
- Pass native types (boolean, int, and others through long double) to a function by value unless they are to be modified by the function. Pass all other objects by reference.
- Put throw specifiers in function definitions in your header files on any methods that throw exceptions. These definitions should begin on a new line directly beneath the function definition.
- If using a namespace in only a single function, use
using just for the scope of that function. Otherwise, use using in the class definition so it's available in the entire file but not for any derived classes or programs using that class.
using namespace std;
- Duplicated code in constructors should be combined into one init() function.
- The default exit codes are
0 if returning successfully, 1 if unsuccessfully. If you need additional exit values, document what they are.
- If an exception terminates a program, makes sure the exception is detailed enough so that the reason it crashed is understood.
- Always catch exceptions if any object you're using can throw one. Be especially careful about catching exceptions in multi-threaded applications as exceptions are local to the thread that throws them.
- For programs, include a -h command-line argument which displays the command syntax. Also display this help when a program is used with incorrect arguments.
- Do not use any deprecated code. (i.e. strstream)
- Use the .emacs file provided with the GPSTk distribution to ensure your emacs-written code is correctly tabbed.
- Do not use '_' except in constants or =#define=s.
Guidelines
- Default constructed objects should be initialized into a bad or unknown state. =enum=s should have an unknown state (or some other name) which is used in the default case.
- Always check if an uninitialized object is used as if it was initiallized. Throw an exception if this happens.
7 Doxygen Comments
Note that Doxygen comments always start with either a
/// or
/**. In addition, within comments there are various directives that begin with
@.
Standards
- Include a
@file directive at the top of each file.
/**
* @file DayTime.hpp
* gpstk::DayTime - encapsulates day and time-of-day in many formats
*/
- Include a comment describing each newly-defined namespace.
/// Robust statistics
namespace Robust
{
// code ...
} // end namespace Robust
- Document each class definition.
/**
* class DayTime, a time representation class for all common time
* formats, including GPS. There is a seamless conversion between
* dates, times, ...
*/
class DayTime ...
- Document each new exception class.
/**
* @ingroup exceptionclass
* DayTime basic exception class.
*/
NEW_EXCEPTION_CLASS(DayTimeException, gpstk::Exception);
- Document enumerations by including
/**< ... */ or /// for each member.
/// The various time frames
enum TimeFrame
{
Unknown, /**< unknown time frame */
UTC, /**< Coordinated Universal Time (e.g., from NTP) */
LocalSystem,/**< time from a local system clock */
GPS_Tx, /**< GPS transmit Time (paper clock) (e.g., 15 smooth) */
GPS_Rx, /**< GPS receive time (paper clock) */
// (e.g., rx data if clock bias is applied)
GPS_SV, /**< SV time frame (e.g., 211 1.5s/6packs) */
GPS_Receiver/**< Receiver time (e.g., 30s, raw 1.5s) */
};
- Document all class member data.
long jday; //< integer 'Julian day', = JD+0.5
long mSod; //< integer milliseconds of the day
double mSec; //< double fractional milliseconds (mSec < 1.0)
double tolerance; //< double tolerance used in comparisons (seconds)
TimeFrame timeFrame; //< see #TimeFrame
- Document each function with a brief description, followed by more detail, if needed. Include the
@param directive to document each calling parameter, or argument, of the function and a @return directive to document the return value.
/** Compute median of an array of length nd;
* array is assumed sorted in ascending order.
* @param xd array of data.
* @param nd length of array xd.
* @return median of the data in array xd.
*/
template <typename T>
T Median(const T *xd, const int nd)
throw(gpstk::Exception)
{
// code ...
} // end Robust::Median()
- Group the code appropriately by using the
@defgroup and @addtogroup directives.
/** @defgroup timegroup GPStk Time Group */
//@{
//code...
//@}
/** @addtogroup timegroup */
//@{
//code...
//@}
- Use HTML directives whenever they will clarify, e.g. in tables.
/**
* Format this time into a string.
* Generate and return a string containing a formatted
* date, formatted by the specification \c fmt.
* \li \%Y year()
* \li \%y year() % 100
* \li \%m month()
* (etc...)
*/
Appendix A: Header File Example
#pragma ident "$Id: GPSTkCodingStandard.txt,v 1.2 2006/05/25 20:21:00 RjBroderick Exp www-data $"
// Class Moo. Make this a brief description and save the long one for
// the DOXYGEN comments below.
#ifndef MOO_HPP
#define MOO_HPP
//Don't put DOXYGEN comments here, they'll be included with whatever follows
#include <string>
#include <map>
#include "moo.h"
/// Namespace farm is for FARM stuff.
namespace farm
{
/// A Moo class. Derives from Cow. More description here.
class Moo : public Cow
{
/// This shows up under the namespace page.
using namespace std;
public:
/// The Max Number. This is how to do a one line comment for DOXYGEN.
static const int MAX_NUMBER = 5;
/**
* An moo type enumeration.
* Notice the syntax of the comments next to the enum values.
*/
enum mooType
{
unknown, ///< an unknown mooType
Short, ///< a short moo
Long ///< a long moo
};
/**
* A default constructor.
* More info goes here about its initial value.
*/
Moo();
/**
* A constructor taking a num and a string.
* @param num This is the syntax for comments on function parameters.
* @param str is what the moo sounds like.
* You can add more description here.
*/
Moo(const int num,
const string& str);
/**
* A constructor taking only a string.
* @param str a message.
* The string is decoded and assigned to this Moo.
*/
Moo(const string& str);
/**
* The Moo destructor.
*/
~Moo();
/**
* Returns anotherNum from the object.
* @return anotherNum
*/
inline int getAnotherNum() const
{return anotherNum;}
/**
* Assigns anotherNum in the object.
* @param num the new value of anotherNum
* @return the modified object
*/
inline Moo& setAnotherNum(const int num)
{anotherNum = num; return *this;}
/**
* Returns someString from the object.
* @return someString
*/
inline string getSomeString() const
{return someString;}
/**
* Assigns someString in the object.
* @param str the new value of someString
* @return the modified object
*/
inline Moo& setSomeString(const string& str)
{someString = str; return *this;}
/**
* Does something.
* @throw SomeException when it can't do something silly.
* @return an integer
*/
int doSomethingSilly()
throw(SomeException);
protected:
/// The number of moos.
int anotherNum;
/// What the moos sound like.
string someString;
private:
/// Disallow the copy constructor.
Moo(const Moo& right);
/// Disallow the assignment operator.
Moo& operator= (const moo& right);
} // end class Moo
}; // end namespace
#endif // MOO_HPP
Discussion