wiki:C++Standard/NamingConventions
Last modified 6 years ago Last modified on 03/08/2013 12:51:22 PM

Draft for new release V 1.6

This document is being updated to reflect changes approved by the DM TCT. The document is in transition.

The previous official version of the C++ Programming Style Guildelines (V 1.5) is available here.

LSST Data Management: C++ Programming Style Guidelines


Table of Contents

  1. Draft for new release V 1.6
  2. 3 Naming Conventions
    1. 3.1 General Naming Conventions
      1. 3-0. Guidance on Selecting Names
      2. 3-1. Names of user defined types MUST be in mixed case starting with …
      3. 3-2. Variable names MUST be in mixed case starting with lower case.
      4. 3-3. Named constants (including enumeration values) MUST be all uppercase …
      5. 3-4. Names representing methods or functions SHOULD be naturally describe …
      6. 3-5. A name representing a typedef MUST be initial letter capitalized, …
      7. 3-5a. A name representing a typedef SHOULD have a 'T' suffix if and only …
      8. 3-6. Names representing namespaces MUST be camelCase with leading …
      9. 3-7. Names representing template types MAY be a single uppercase letter or …
      10. 3-8. Abbreviations and acronyms MUST not be uppercase when used as name.
      11. 3-9. Global variables and functions SHOULD be avoided and if used MUST …
      12. 3-10. Private class variables and methods MUST have underscore prefix.
      13. 3-11. Generic variables MAY have the same name as their type.
      14. 3-12. All names MUST be written in English and use American English …
      15. 3-13. Variables with a large scope SHOULD have long names, variables with …
      16. 3-14. The name of the object is implicit, and SHOULD be avoided in a …
      17. 3-37 Names representing containers of numeric STL built-ins MUST be of the …
      18. 3-37a Names representing containers of DM objects SHOULD be of the form: " …
    2. 3.2 Specific Naming Conventions
      1. 3-15. The terms 'get/set' MUST be used where an attribute is accessed …
      2. 3-16. (Deleted)
      3. 3-17. The term 'find' SHOULD be used in methods where something is looked …
      4. 3-18. The term 'initialize' SHOULD be used where an object or a concept is …
      5. 3-19. Variables representing GUI components SHOULD be suffixed by the …
      6. 3-20. The suffix 'List' MAY be used on names representing a list of …
      7. 3-21. The prefix 'n' SHOULD be used for variables representing a number of …
      8. 3-22. The filter name prefix (ugrizy) MUST be used if and only if the name …
      9. 3-23. Iterator variables SHOULD be declared within the loop scope.
      10. 3-24. Names for boolean variables and methods SHOULD be obviously boolean.
      11. 3-25. Complement names MUST be used for complement operations.
      12. 3-26. Abbreviations in names SHOULD be avoided.
      13. 3-27. Deleted
      14. 3-28. Negated boolean variable names MUST be avoided.
      15. 3-29. Enumeration constants MAY be prefixed by a common type name if at …
      16. 3-30. Exception classes SHOULD be suffixed with Exception.
      17. 3-31. Functions (methods returning something) SHOULD be named after what …
      18. 3-32. Parameters in functions SHOULD be declared in order of output, …
      19. 3-33. (Deleted)
      20. 3-34. Uncertainty values associated with a variable SHOULD be suffixed by …
      21. 3-35. Unused.
      22. 3-36. (Deleted)

3 Naming Conventions

3.1 General Naming Conventions

3-0. Guidance on Selecting Names

  • The fundamental quantity being described should appear first in the name, with modifiers concatenated afterward. A rule of thumb is to ask what the units of the quantity would be, and make sure that quantity appears first in the name.
    • "dateObs", not "obsDate" for a quantity that fundamentally is a date/time of significance;
    • "timeObsEarliest" (or, "timeObsFirst"), not "earliestObsTime"
    • "nGoodPix" not "goodPixN" since this is fundamentally a number
    • There are some historical exceptions (e.g., expTime from the FITS standard) that must be preserved
  • Use care to select the most meaningful name to represent the quantity being described
    • "imageMean" not "pixelMean" if we are talking about the mean value of an image, not repeated measurements of a pixel
  • Names should not explicitly include units
    • "skyBackground" not "skyADU" to indicate the sky background level
    • "expMidpoint" rather than "taiMidPoint"; or "timeRange" not "taiRange"
  • Acronyms should be used sparingly, and limited to very common usages in the relevant community.
    • CCD, FWHM, ID, PSF, and RA would be fine as name fragments
  • Obscure abbreviations should be avoided: clarity is probably more important than brevity.
    • "apertureDiam" would be better than "apDia"
  • The Database Schema document should be reviewed for existing appropriate names
    • Check the authoritative DB Column names for the current Project in order to select consistent names between persisted C++ variables and their corresponding DB Columns. Refer to Section 3.3 Names Exposed to Database.

3-1. Names of user defined types MUST be in mixed case starting with uppercase.

class Line, SavingsAccount

struct {
    float bar;
    int  yoMama;
} Foo;
Foo myFoo;

typedef Vector<Frame> FrameVector;

Common practice in the C++ development community. The capitalization rule for class names should be all words in the name capitalized, e.g., "ClassName".

3-2. Variable names MUST be in mixed case starting with lower case.

int lineWidth;

Common practice in the C++ development community. Makes variables easy to distinguish from types, and effectively resolves potential naming collision as in the declaration Line line;. Keep variable names balanced between short and longer, more meaningful. Use 8 to 20 characters as a guideline (excluding integer loop counters which may be as little as 1 character).

3-3. Named constants (including enumeration values) MUST be all uppercase using underscore to separate words.

Common practice in the C++ development community.

int const MAX_ITERATIONS = 25;
int const MIN_ITERATIONS(23);
enum { HIGH_SCHOOL, GRAMMAR_SCHOOL, KINDEGARTEN };

In general, the use of such constants should be minimized. In many cases implementing the value as a method is a better choice:

int getMaxIterations() {     // NOT: int const MAX_ITERATIONS = 25
  return 25;
}

This form is both easier to read, and it ensures a unified interface towards class values.

Note that this rule applies only to const variables that represent constants (i.e. those that would be set using an enum or #define in C); it does not apply to variables that happen to be determined at their point of definition, e.g.

void foo(string const& filename);
float const r2 = r*r;           // radius^2

3-4. Names representing methods or functions SHOULD be naturally describe the action of the method (and so will usually start with a verb) and MUST be written in mixed case starting with lowercase. Private member function names MUST additionally lead with an underscore.

Do not put a space between the function name and the opening parenthesis when declaring or invoking the function.

class GoodClass {
public:
    void const getPublic() {}           // OK
protected:
    void const getProtected() {}        // OK
private:
    void const _getPrivate() {}         // OK
};

void getName() {...}                    //OK
void computeTotalWidth() {...}          //OK

Refer to Rule 3-10 for a discussion on the leading underscore requirement for private member functions.

Common practice in the C++ development community. This is identical to variable names, but functions in C++ are already distinguishable from variables by their specific form.

3-5. A name representing a typedef MUST be initial letter capitalized, camel-case with no prefix of the enclosing class.

typedef unsigned char Byte;
typedef unsigned long BitMask;
Byte smallMask;

This syntax is consistent with template type names and classes which are also similar in usage.

3-5a. A name representing a typedef SHOULD have a 'T' suffix if and only if necessary to disambiguate the typedef from a template bare name

If the typedef is a template specialization of a concrete type, the typedef name should typically include some indication of the parameter type (e.g. "typedef Image<float> ImageF;"). If the specialization uses an incoming template parameter, the suffix 'T' is preferred to using the specialized template's bare name, as the latter is very difficult to use correctly in C++.

3-6. Names representing namespaces MUST be camelCase with leading lowercase letter and based on component or directory name.

The original package developer will specify in the .cc file the preferred abbreviation to use and, optionally, also use it throughout his code. The original developer may consider using the following guideline to fabricate the name

  • remove the preliminary 'lsst';
  • concatenante the remaining fields;
  • if desired to make shorter, abbreviate each field while still maintaining a relevant word.
    namespace pexLog = lsst:pwx:logging;
    namespace afwMath = lsst::afw::math;
    

Two options are available for using a namespace when defining symbols

  • Specify the namespace explicitly in the definition
    lsst::foo::bar::myFunction(...) {...};
    
  • Use an abbreviation for the namespace
    namespace fooBar lsst::foo::bar;
    fooBar::myFunction(...) {...};
    
  • Putting the definitions into a namespace block is not recommended
    namespace lsst{
    namespace foo{
    namespace bar{     // NOT Recommended
       myFunction(...) {...};
    }}}  // lsst::foo::bar
    

3-7. Names representing template types MAY be a single uppercase letter or a mixed case phrase, first letter capitalized in each word, indicating the desired type.

template<typename T> ...             // acceptable
template<typename T> ...                // acceptable
template<typename C, typename D> ... // acceptable
template<class PixelType> ...  // acceptable, user-defined class only

Common practices in the C++ development community. This makes template names stand out relative to all other names used. Note this rule applies to the template type, not template instances. Regarding the use of typename versus class, we will adopt the convention of using typename in all cases except where the intent is ONLY a user-defined class and not primitives.

It is recommended that template parameter names that are not a single character be suffixed with 'T' or 'Type' to distinguish them from other, concrete types.

3-8. Abbreviations and acronyms MUST not be uppercase when used as name.

exportHtmlSource();    // NOT: exportHTMLSource();
openDvdPlayer();       // NOT: openDVDPlayer();

Using all uppercase for the base name will give conflicts with the naming conventions given above. A variable of this type would have to be named dVD, hTML etc. which obviously is not very readable. Another problem is illustrated in the examples above; When the name is connected to another, the readability is seriously reduced; the word following the abbreviation does not stand out as it should.

3-9. Global variables and functions SHOULD be avoided and if used MUST always be referred to using the '::' operator.

::mainWindow.open(), ::applicationContext.getName(), ::erf(1.0)

In general, the use of global variables should be avoided. Consider using singleton objects instead. Only use where required (i.e. reusing a framework that requires it.) See Rule 5-7.

Global functions in the root namespace that are defined by standard libraries can often be avoided by using the C++ versions of the include files (e.g. "#include <cmath>" instead of "#include <math.h>"). Since the C++ include files place functions in the std namespace, "using namespace std;", which is permitted by Rule 5-41, will allow these functions to be called without using the '::' operator. In cases where functions are only available in the C include files, the '::' operator must be used to call them. This requirement is intended to highlight that these functions are in the root namespace and are different from class methods or other namespaced free functions.

3-10. Private class variables and methods MUST have underscore prefix.

TBD In the future, Gregory will add commentary on restrictions regarding single letter private functions

class SomeClass
{
private:
    int  _length;
    int  _computeBlob();
}

Apart from its name and its type, the scope of a variable or method is its most important feature.

Indicating class scope by using underscore makes it easy to distinguish class variables from local scratch variables. This is important because class variables are considered to have higher significance than method variables, and should be treated with special care by the programmer. A side effect of the underscore naming convention is that it nicely resolves the problem of finding reasonable variable names for setter methods and constructors:

void setDepth(int depth){
    _depth = depth;
}

An issue is whether the underscore should be added as a prefix or as a suffix. Both practices are commonly used. Since LSST Data Management uses both C++ and Python as implementation languages, prefixing the underscore is recommended in order to maintain conformity with Python's naming convention where variables and functions with leading underscore are treated specially. Care must be given to avoid using a reserved name.

It should be noted that scope identification has been a controversial issue for quite some time. It seems, though, that this practice now is gaining acceptance and that it is becoming more and more common as a convention in the professional development community.

3-11. Generic variables MAY have the same name as their type.

void setTopic(Topic *topic)      // NOT: void setTopic (Topic *value)
                                  // NOT: void setTopic (Topic *aTopic)
                                  // NOT: void setTopic (Topic *x)

void connect(Database *database) // NOT: void connect (Database *db)
                                  // NOT: void connect (Database *oracleDB)

Reduce complexity by reducing the number of terms and names used. Also makes it easy to deduce the type given a variable name only.

If for some reason this convention doesn't seem to fit it is a strong indication that the type name is badly chosen.

Non-generic variables have a role. These variables can often be named by combining role and type:

Point startingPoint, centerPoint;
Name  loginName;

3-12. All names MUST be written in English and use American English spelling.

int fileName;    // NOT:   int filNavn;
int color;       // NOT:   int colour;

English is the preferred language for international development.

3-13. Variables with a large scope SHOULD have long names, variables with a small scope MAY have short names.

Scratch variables used for temporary storage or indices are best kept short. A programmer reading such variables should be able to assume that its value is not used outside a few lines of code. Common scratch variables for integers are i, j, k, m, n and for characters c and d.

3-14. The name of the object is implicit, and SHOULD be avoided in a method name.

line.getLength();    // NOT:  line.getLineLength();

The latter seems natural in the class declaration, but proves superfluous in use, as shown in the example.

3-37 Names representing containers of numeric STL built-ins MUST be of the form: " [capitalized STL name][element type suffix used in afw::image] ".

std::vector<double> => VectorD
std::list<int> => ListI

3-37a Names representing containers of DM objects SHOULD be of the form: " [element class name, ignoring ptrs][capitalized STL name] ".

std::vector<PTR(Span)> => SpanVector
std::list<Box2I> => Box2IList
  • However, containers which have a clear meaning in a particular context, (e.g. MaskPlaneDict), MAY use a name that describes that meaning (like MaskPlaneDict).
  • Or if, for example, a container is logically a list (i.e. doesn't need random access) but is actually a std::vector for simplicity/performance reasons, it may be called a 'List', especially to preserve backwards compatibility.

3.2 Specific Naming Conventions

3-15. The terms 'get/set' MUST be used where an attribute is accessed directly and in the imperative form. The variable name portion should be the same as the actual variable name but now with the first letter capitalized.

employee.getName();       matrix.getElement (2, 4);
employee.setName(name);  matrix.setElement (2, 4, value);

Common practice in the C++ development community. In Java this convention has become more or less standard. Methods that return a reference to an object for which "set" has no meaning, should not follow this convention. For instance, use

Antenna().Drive().getFoo()

rather than

getAntenna().getDrive().getFoo()

3-16. (Deleted)

3-17. The term 'find' SHOULD be used in methods where something is looked up.

vertex.findNearestVertex();   matrix.findMinElement();

Give the reader the immediate clue that this is a simple look up method with a minimum of computations involved. Consistent use of the term enhances readability.

3-18. The term 'initialize' SHOULD be used where an object or a concept is established.

printer.initializeFontSet();

The American 'initialize' should be preferred over the English 'initialise'. Abbreviation 'init' should be avoided.

3-19. Variables representing GUI components SHOULD be suffixed by the component type name.

mainWindow, propertiesDialog, widthScale, loginText, leftScrollbar, mainForm, fileMenu, minLabel, exitButton, yesToggle etc.

Enhances readability since the name gives the user an immediate clue of the type of the variable and thereby the object's resources.

3-20. The suffix 'List' MAY be used on names representing a list of objects.

Vertex (one vertex),   vertexList (a list of vertices)

Enhances readability since the name gives the user an immediate clue of the type of the variable and the operations that can be performed on the object.

Simply using the plural form of the base class name for a list (matrixElement (one matrix element), matrixElements (list of matrix elements)) should be avoided since the two only differ in a single character and are thereby difficult to distinguish.

A list in this context is the compound data type that can be traversed backwards, forwards, etc. (typically an STL vector ). A plain array is simpler. The suffix 'Array' can be used to denote an array of objects.

3-21. The prefix 'n' SHOULD be used for variables representing a number of objects.

nPoints, nLines

The notation is taken from mathematics where it is an established convention for indicating a number of objects.

3-22. The filter name prefix (ugrizy) MUST be used if and only if the name is specific to a filter.

For example:

float iAmplitude, iPeriod;   // OK
float gAmplitude, gPeriod;   // OK
int iLoopCtr;                // BAD

This recommendation fosters consistent naming of C++ and DB shared persistent objects which use a filter-initial prefix.

Naming DB persistent objects by incorporating their filter band fosters the efficiency of a simple sort rule. If the C++ data is maintained in an array indexed by filter, this rule doesn't apply. See also Rule 3-36

3-23. Iterator variables SHOULD be declared within the loop scope.

for (vector<MyClass>::iterator listIter = list.begin(); listIter != list.end(); listIter++){
    Element element = *listIter;
    ...
}

It is not always possible to declare iterator variables in scope (for example if you have several iterators of different type), but do it when you can. Declare additional iterator variables just before the loop, so it's clear that they are associated with the loop.

3-24. Names for boolean variables and methods SHOULD be obviously boolean.

Examples of good names include

bool isSet, isVisible, isFinished, isFound, isOpen;
bool exists();
bool hasLicense(), canEvaluate(), shouldSort()

Common practice in the C++ development community and partially enforced in Java.

Using the 'is' prefix can highlight a common problem of choosing bad boolean names like 'status' or 'flag' . 'isStatus' or 'isFlag' simply doesn't fit, and the programmer is forced to choose more meaningful names.

3-25. Complement names MUST be used for complement operations.

get/set, add/remove, create/destroy, start/stop, insert/delete, increment/decrement, old/new, begin/end, first/last, up/down, min/max, next/previous, old/new, open/close, show/hide, suspend/resume, etc.

Reduce complexity by symmetry.

3-26. Abbreviations in names SHOULD be avoided.

computeAverage();     // NOT:  compAvg();

There are two types of words to consider. First are the common words listed in a language dictionary. These must never be abbreviated. Write:

  • command instead of cmd
  • copy instead of cp
  • point instead of pt
  • compute instead of comp
  • initialize instead of init

etc.

Then there are domain specific phrases that are more naturally known through their abbreviations/acronym. These phrases should be kept abbreviated. Write:

  • html instead of HypertextMarkupLanguage
  • cpu instead of CentralProcessingUnit
  • ccd instead of ChargeCoupledDevice

etc.

3-27. Deleted

3-28. Negated boolean variable names MUST be avoided.

bool isError;    // NOT:   isNoError
bool isFound;    // NOT:   isNotFound

The problem arises when such a name is used in conjunction with the logical negation operator as this results in a double negative. It is not immediately apparent what !isNotFound means.

3-29. Enumeration constants MAY be prefixed by a common type name if at global (or namespace) scope.

enum { GRADE_HIGH, GRADE_MIDDLE, GRADE_LOW };

Where possible, put enums in appropriate classes, in which case the GRADE_ isn't needed:

class Grade {

enum { HIGH, MIDDLE, LOW };

Grade() {} Etc. ...

};

This gives additional information of where the declaration can be found, which constants belongs together, and what concept the constants represent.

3-30. Exception classes SHOULD be suffixed with Exception.

class AccessException {
  ...
}

Exception classes are really not part of the main design of the program, and naming them like this makes them stand out relative to the other classes.

3-31. Functions (methods returning something) SHOULD be named after what they return and procedures (void methods) after what they do.

double & getElevation(unsigned int antennaId), void pointAntenna(Source const & source)

Increase readability. Makes it clear what the unit should do and especially all the things it is not supposed to do. This again makes it easier to keep the code clean of side effects.

3-32. Parameters in functions SHOULD be declared in order of output, input, default input.

Keeps inputs together and when SWIG is used avoids moving output parameters out of the middle of the list.

3-33. (Deleted)

3-34. Uncertainty values associated with a variable SHOULD be suffixed by one of 'Var', 'Cov', 'Sigma'.

There is no universal suffix for uncertainties; i.e. no 'Err' suffix will be used.

The cases that we have identified, and their appropriate suffixes, are:

  • Standard deviation: 'Sigma' (not Rms as rms doesn't imply that the mean's subtracted)
  • Covariance: 'Cov'
  • Variance: 'Var'
float xAstrom;          // x position computed by a centroiding algorithm
float xAstromSigma;     // Uncertainty of xAstrom
float yAstrom;
float yAstromSigma;
float xyAstromCov;

The postfix 'Err' can easily be misinterpreted as error flags. Use the full 'Sigma' since 'Sig' can easily be misinterpreted as 'Signal'

3-35. Unused.

3-36. (Deleted)