Skip to content

Vapor Coding Convention

sgpearse edited this page Nov 17, 2021 · 11 revisions

VAPOR Coding Convention

To improve readability and consistency in the code, we have adopted the following coding conventions. Many of these conventions are adapted from the those of the VTK and VTK-m project.

Source Files

  1. All code must be valid by the C++11 specification.
  2. All code must be compatible with UCAR's modified BSD license.
  3. Copyright notices should appear at the top of all source, configuration, and text files. The statement should have the following form (with the year replaced with the year the file was created):
//*************************************************************
// 
// Copyright (C) 2016 
// University Corporation for Atmospheric Research
// All Rights Reserved 
// 
// *************************************************************
  1. All include files should use include guards starting right after the copyright statement. The naming convention of the include guard macro is that it should start with the namespace (if any), followed by an _, followed by the file base name, followed by _H, all in caps. For example:
#ifndef VAPOR_PARAMSMGR_H
#define VAPOR_PARAMSMGR_H
.
.
#endif
  1. Include statements should be ordered with system/external dependencies first, and local dependencies last. The rationale for this is that if there are compilation conflicts the compiler will report the error in the "local" file.

  2. The indentation follows the Allman style. The curly brace (scope delimiter) for a block is placed on the line following the prototype or control statement and is indented with the outer scope (i.e. the curly brace does not line up with the code in the block). Conditional clauses (including loop conditionals such as for and while) must be in braces below the conditional. That is, instead of

    if (test) {clause; }

    use

    if (test) 
    {
      clause; 
    } 
    • The rational for this requirement is to make it obvious whether the clause is executed when stepping through the code with the debugger. The one exception to this rule is when the clause contains a control-flow statement with obvious side effects such as return or break.
  3. Limit all lines to 100 characters whenever possible. Avoid deeply nested clauses and limit the number of lines in function/method to approximately one "page". Use two space indentation or tabs. Be consistent throughout the file.

  4. Whenever possible, use templates to resolve data types like float, double, or vectors to make code as flexible as possible.

Name Spaces

  1. VAPOR has several namespaces. The declaration of each namespace should be on its own line, and the code inside the namespace bracket should not be indented.

  2. References to classes and functions should be fully qualified with the namespace. This make it easier to establish classes and functions from different packages and to find source and documentation for the referenced class. The global using namespace statement should be avoided. Instead for convenience use:

    using namespace std;

    For example:

    using std::vector;

Classes

  1. Any public class should generally be declared in its own header file, and defined in its own source file. The class name and file names should match, including case. Use camel case for naming (first letter of each word capitalized, e.g. ThisIsMySuperCoolClass). The file name should be in a directory whose name matches the name space. There are several exceptions to this rule:

    • Templated classes and template specialization often require the implementation of the class to be broken into pieces. Sometimes a specialization is placed in a header with a different name.
    • Many VAPOR features are not encapsulated in classes. Functions may be collected by purpose or co-located with associated class.
    • Groups of small, related classes may be grouped in the same file.
  2. Use only alphanumeric characters in names. Use capitalization to demarcate words within a name (camel case). The exception is preprocessor macros and constant numbers that are, by convention, represented in all caps and a single underscore to demarcate words.

  3. All public class methods and members should use PascalCase. E.g. GetValue().

  4. All private class method and members should be prefixed with an underscore, have a lower-case first letter, and use camel case for subsequent words. E.g. _getValue().

  5. Classes are documented with Doxygen-style comments before classes, methods, and functions.

  6. Exposed classes should not have public instance variables outside of exceptional situations. Access is given by convention through methods with names starting with Set and Get or through overloaded operators.

  7. Static classes should not be used outside of exceptional circumstances.

  8. Multiple inheritance is strongly discouraged in VAPOR classes, with the notable exception of Qt GUI classes that inherit from auto-generated Qt classes.

Debug/Comment

  1. Debug statements must be removed before merging into master branch.

  2. Comment of code must be removed before merging into master branch. That is, the only two cases where comment is allowed are

    • The comment is for Doxygen documentation;
    • The comment documents program logic. For example,
    if( returnValue != 0 )    // when statistics calculation fails
    { }
    else                      // now we present the statistics to the user
    { }
  3. Blocks of functionalities that are potentially useful in the future can live in a macro that isn't get compiled. This macro, however, must be named after the VAPOR version number at that particular time, and subject to removal at a later time.

    • For example, when developing VAPOR version 3.0 beta, you improved a function RenderBlock() with a new implementation, but wanted to keep the old implementation for a while. Because the old implementation comes from VAPOR version 3.0 alpha, then you use a following macro:
    #ifdef VAPOR3_0_0_ALPHA
    RenderBlock()
    { }
    #endif

Doxygen Documentation

All public functions and member objects need to be documented using Doxygen comments. When documenting functions, these comments should include descriptions of input variables as well as a description of a return value. Take the following public RenderParams function for example:

//! Specify the variable being used for color mapping
//! \param[in] string varName. If any \p varName is "0" it
//! will be quietly
//! set to the empty string, "".
//
virtual void SetColorMapVariableName(string varname);

Public members such as tags in the RenderParams class also need to be documented with Doxygen style comments. For example:

//! If a renderer supports rotation about a point of origin,
//! this string identifies the parameter for the origin's
//! location on the X axis. 
static const string RenderParams::XOriginTag;

See this document for information on how to build Doxygen documentation.

Miscellaneous

Here are miscellaneous points every developer should pay attention during the development cycle.

  1. C-style cast should be avoided when the following criteria are met:
    1. It is applied on a pointer;
    2. This pointer is pointing to a C++ object. As an example, avoid writing code
WireFrameParams* p = (WireFrameParams*)GetActiveParams(); 

but write it the following way:

WireFrameParams* p = dynamic_cast<WireFrameParams*>(GetActiveParams());
assert( p ); 
  1. Trailing return types should be avoided due to resistance to changes.