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 use '_' in identifiers (functions, classes, variables).

  • 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.

  • 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.
       bool isMoo() const;
       bool hasACow() const;

Guidelines

  • Do not use "magic values" for loops. Instead, define a descriptive constant.
       const int NUM_COWS = 2;
       if(i < NUM_COWS)

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.

Guidelines

  • Name the file with main() the same as the executable name.

3 Code Layout

Standards

  • 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
       }

  • 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. (TODO Need to amplify this by better defining what a statement is). 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.

Guidelines

  • Always use braces on if...else, while and for blocks, even if only one line is in the block.
  • 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))

  • Limit the line length to eighty (80) characters. Continue longer lines on the next line, indenting three (3) spaces.

4 Submission Requirements

Standards

  • Only use features present in the C++03 and earlier standards.

Guidelines

  • Library code does not produce debugging text output unless request (i.e. seeing a debug flag).

  • All data objects (where appropriate) should have a STDOUT operator.

5 Code Comments

Standards

  • Doxygen shall used for processing inline comments into formal GPSTk documentation. Appendix A shows an example header file with Doxygen compliant comments.

  • Comments at the start of a file are left justified.

  • 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. See indentation standards in Code Layout above

  • 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 (bool, 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;

  • The default exit codes are 0 if returning successfully, 1 if unsuccessfully. If you need additional exit values, document what they are.

  • 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)

Guidelines

  • Default constructed objects should be initialized into a bad or unknown state. enum types 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 initialized. Throw an exception if this happens.

  • Duplicated code in constructors should be combined into one init() function.

  • 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.

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
           */

  • 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, including units where applicable.
       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...
          //@}

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

Issues I've run across:
  1. std::ostream operator functions should be declared outside of any namespace
  2. "using namespace" statements should never ever be in include files
  3. The implementation of methods inside include files is pretty out of hand in a lot of cases. Move code into .cpp when possible.
  4. Declare methods const unless they're actually modifying the object.
  5. Library classes that are not in any namespace whatsoever
  6. Using things like "unsigned short" where "uint16_t" and the like would have been much more appropriate.
    • This is particularly true of binary encoded data. "unsigned short" is only guaranteed to be at least 16 bits, not exactly. It could be larger. Any binary encoded data should be defined using uint16_t and the like.
  7. Using signed quantities where they should be unsigned.
  8. Comments spaced out to some arbitrary personally chosen right margin
  9. Largely undocumented library code

-- JohnKnutson - 04 Dec 2015
 
Topic revision: r7 - 15 Dec 2015, JohnKnutson
 

This site is powered by FoswikiCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding Foswiki? Send feedback