Coding and Design Standards - nasa/gunns GitHub Wiki

Coding Standards

C++ Code Standards

GUNNS was initially written under the TS21 coding standards. The full TS21 C++ coding stadards document is here. During development under the TS21 project from 2010-2014, these standards were quite strictly followed and enforced through peer review. Since 2014, we've tried to follow these standards in spirit, but have gradually bent or abandoned some.

This page summarizes the most important rules, and applies a few 'house rules' we use for GUNNS that override the TS21 standards. The standards listed below supercede the TS21 standards.

Note that we violate some of these in special cases where it is justifiable.

  1. Header files use file extension .hh, and body files use .cpp. No exceptions - our makefiles assume this convention, and files that use different extensions are ignored or broken.
  2. One 'class' per file. Each .hh/.cpp file pair should define only one class. We make an exception for input and config data classes belonging to the main class, and for similar small structures or classes that are dedicated for use by the main class.
  3. Only one body file per header. Having a class defined in multiple .cpp body files leads to great confusion. Also this would break our makefiles.
  4. File name must match the main class name. For example, GunnBasicLink.hh and .cpp define main class GunnsBasicLink.
  5. Use CamelBack names. For example, ThisIsAClass and thisIsAVariable.
  6. Type names are capitalized. Classes, structs, namespaces, enums, etc. have a capital letter for the first letter.
  7. Attributes are lower-case. All variables and functions begin with a lower-case letter.
  8. Member variables begin with 'm'. This stands for 'member' and distinguishes between member and local variables. For example 'mMass' is a member variable, 'mass' is local.
  9. All GUNNS classes should begin with name 'Gunns', followed by their aspect. For example, GunnsFluidThing or GunnsBasicThingy. At the time GUNNS development began, Trick (Trick 7 and early Trick 10) did not fully support C++ namespaces yet, so we adopted the 'Gunns' name convention in lieu of namespaces to avoid name collisions with other projects.
  10. All lines should be limited to 100 characters or less. It's okay to go over slightly when it is more clearly readable than breaking it up into multiple lines.
  11. Do not use the ? operator. Do it long form with curly braces and if/else instead.
  12. Do not use inline if and loop statements. For example, do not do this: if (thing) return; or for (int i=0; i<1; ++i) doStuff; Do it long form with curly braces instead.
  13. No magic numbers. All constants should be defined as static const class members. For example we don't inline pi = 3.14159 in the code. The only exceptions are 0, 1, 2, and 3.
  14. Inline functions when appropriate. Small function of just a few lines are automatically inlined by the compiler, so define them in the .hh file with the inline keyword. Use the LCOV code coverage in the unit test to check that the code inline-ness is matching what the compiler does.
  15. All comments should be compatible with Doxygen. We don't really use Doxygen anymore, because as programmers we tend to get more out of just looking at the code directly. However, the GUNNS code base was always written to support Doxygen, so we should keep doing it in case we or our users ever want to make use of it.
  16. Rule of 3: all classes should explicitly declare the constructor, destructor, and copy constructor. We also extend this to the assignment operator. Declare these as private and un-implemented as appropriate to deny their use.
  17. Destructors should be virtual.
  18. Adhere to the C++98 standard. Although later standard releases and extensions like Boost have a lot of nice features, using them always invites portability and dependency problems for at least one user. At some point in the future we may upgrade to a newer standard and impose that on all users.
  19. That which allocates, deletes. Any object that allocates dynamic memory should be the one that frees it.
  20. Do not use ../ in include paths. All include paths should be the full spelled-out path from either the gunns/, gunns/ms-utils/, or gunns/gunns-ts-models/ folders. For example, gunns/core/test/UtGunnsBasicLink.hh includes core/GunnsBasicLink.hh not ../GunnsBasicLink.hh.
  21. Remain consistent with the existing code style. Please honor the style used throughout the rest of GUNNS. This helps us all read and maintain it. Even if it's not your style, it helps everyone if you adhere to it. This applies to comments and whitespace as well as the executable code.
  22. No tabs, only spaces for whitespace. Tabs are evil because different code editors display them at different widths, misaligning the visual indentation when there is a mix of spaces and tabs in the code. Configure your editor to replace tabs with inserting 4 spaces instead. 4 spaces is the standard indentation used throughout the code base.
  23. No multiple inheritance. This prevents the diamond inheritance problem.
  24. Floating-point literal constants should specify at least one decimal place. This ensures that both the compiler and programmers viewing the code will interpret the literal as a floating-point, not an integer. So this: double x = y * 2.0;, not this: double x = y * 2;
  25. Use const whenever possible.

Extra standards specific to NASA Trick:

  1. Trick headers: Although modern Trick doesn't require some of this stuff, we still keep it for consistency. The header file should define the purpose, requirements, references, assumptions & limitations, library dependencies (see below) and programmers. The programmers section really only needs the initial author; we don't maintain this with new author every time we change the code, since there is a record of authors in Git. Body files should define the library dependencies (see below).

  2. Trick Library Dependency declarations:

    2a. All class headers should declare the dependency on its own compiled object. For example, Gunns.hh should declare the Trick library dependency on Gunns.o

    2b. Dependencies on other compiled objects should be declared in the file where they're called. This is usually the .cpp file. For example, Gunns.cpp declares a library dependency on GunnsBasicLink.o because GunnsBasicLink functions are called from the .cpp file. If Gunns.hh defined inline functions that called functions in GunnsBasicLink, then we would declare the library dependency on GunnsBasicLink.o in Gunns.hh instead of Gunns.cpp. In the case where both the .hh and .cpp files depend on a compiled object, declare the dependency in the .hh file, and Trick carries that over to the .cpp file for you.

  3. Limit all direct dependencies on Trick to software/SimCompatibility/TsSimCompatibility.hh. This helps port GUNNS to non-Trick environments, as that is the only file that has to be changed. All dependencies are in macros. These define the Trick Memory Manager interfaces, and class friending of the Trick input processor and variable server.

  4. All class variable declarations must include the Trick comment. The 'Trick comment' declares specific things for Trick, like the variable server input/output type, units, checkpointing type, and description. We use a special syntax that also makes this compatible with Doxygen. For example: double mVolume; /**< *o (m3) trick_chkpnt_io(**) This is the object volume. */

Python Code Standards

We don't have formal standards for Python. I'm not sure why. We use Python for the Trick sim input files and various offline processing, like the GunnsDraw code auto-generation and integration test system. These are somewhat less important than the C++ built into the user's runtime executable, which is maybe why we've never had a formal standard.

Design Standards

TBD