Introduction

Intended audience

This document addresses those who wish to develop models with LIBPF™ (model developers).

Scope

This document describes the suggested coding standard for LIBPF™, the LIBrary for Process Flowsheeting version 1.0 and UIPF, the User Interface for Process Flowsheeting version 1.0.

Also see (1), (2), (3), (4) and (5) for more generic stuff.

Prerequisites

  • a basic knowledge of the C++11 programming language is required, see (9)

Files

Programming language: use C++11 (8), with no proprietary extensions.

File extensions: use h for headers, and cc for C++ sources.

One file per class, file name same as class name; exceptions: group homogeneous, small classes and classes within namespaces.

File encoding: use 7-bit US-ASCII (we don't allow UNICODE in source files for this reason):

7-bit US-ASCII table

Include guard in header files, use PROJECT_FILENAME_H macro, replace PROJECT substring with {LIBPF, UIPF* and replace FILENAME substring with uppercase file name without extension.

Header file skeleton



Source file skeleton



Types and constants

  • character constants and internal character representation: UTF-8; if you need to encode UNICODE characters in string constants, use L'\u0000' representation to UTF-16 encoded wchar_t - see (9) C.3.3 Large Character Sets. Then convert from wchar_t to internal representation UTF-8 char using ToUtf8 function, see UtfConverter documentation.

  • integers: except for interfacing with external APIs, use int because of (4), “integer types”. Do not use long, short.

  • real numbers: use double precision floating point numbers (double).

  • enumerations: use SmartEnum whenever possible.

Identifier naming conventions

Use camelcase:

  • SmartEnums and classes: upper CamelCase (7): MyClass, MyEnum;

  • variables and function names: lower camelCase (7): myClassInstance, myString.

Acronyms also take camelcase as in: EosBase.

The order of the words is by decreasing importance (so than when classes or identifiers are sorted alphabetically, homogeneous items are naturally grouped), i.e. the reverse w.r.t. the intuitive ordering in English and German.

First example: Donau-Dampf-Schiff-Fahrt-Gesellschaft-Schiffs-Kapitän-Kabine class name should be: KabineKapitaenSchiffGesellschaftFahrtSchiffDampfDonau.

Second example: the instance name for the yellow box is: boxYellow.

Names of variables private or protected to a class: same but postfixed with underscore: i_, val_, bufferTemp_; do not use m_variable convention.

Getter/setter: for getter use variable name, for setter use setVariable.

Comprehensive example:

class MyClass {
private:
   long myVar_;
public:
  long myVar(void);
  void setMyVar(long i);
};
MyClass myInstance;

One-letter uppercase names are preferred for template arguments (T, U, V, E ....).

All-uppercase names of more than one letter are reserved for preprocessor defines.

Follow the guidelines in (10).

In particular:

  • try to avoid warnings messages as far as is reasonably practicable, even when compiled with the highest and most pedantic warning level, avoiding vendor specific extensions if possible

  • gcc: -Wall -Wextra -pedantic -std=gnu++98

  • msvc: /W4

Formatting

Do not use tabs, use two blanks for indenting.

Uncrustify indenting standard: see file uncrustify.cfg, tested with uncrustify 0.48

In declarations and definitions use (void) rather than () for a function without parameters.

For an empty code block use { } not ; or {}.

Comments and documentation

Code reading helps

Use this 120-columns, 3-line separator whenever there are several classes interfaces / implementations in one file:

/*====================================================================================================================*/
/*==  ClassName  =====================================================================================================*/
/*====================================================================================================================*/

Decoration: repeat the member function name after the closing curled bracket, skip return value, templates and arguments unless there are several overloads

template<class T> void MyClass<T>::Member(void) {
} // MyClass::Member
template<class T> void MyClass<T>::Member(int) {
} // MyClass::Member int

Group and highlight the getter/setter members, the pure virtual members, the implementations for parent classes' pure virtual members.

Literate programming

Use doxygen-style literate programming (6), details follow.

  1. In all files:

    • @file

    • @brief

    • @author

  2. In header files:

    • for each class, brief description using the special C++ style on-line comment with ///

      /// Only a short description for the class
      
    • for each method, brief description using the special postponed C++ style on-line comment with ///<

      long var; ///< Only a short description after the member
      
  3. In source files:

    • (if applicable) for a class, detailed description using a block of at least two C++ comment lines, i.e. the a C++ style multiline comment with ///

      /// ... text ...
      /// ... text ...
      
    • (if applicable) for a method:

      • detailed description using the special C++ style multiline comment with ///

      • return using @return

      • parameter documentation using @param

      • documentation on thrown exceptions using @exception

Cpplint

Use cpplint from (4), but skip useless tests, i.e.:

../scripts/cpplint.py filename.cc 3>&1 1>&2 2>&3 | grep -v 'whitespace/comments' | grep -v 'build/include' | grep -v 'whitespace/line_length' | grep -v 'whitespace/comma' | grep -v 'runtime/rtti' | grep -v 'runtime/references' | grep -v 'whitespace/semicolon' | grep -v 'readability/streams'

The following rules must be enforced:

  • Do not use namespace using-directives. Use using-declarations instead. [build/namespaces], except in the implementation inside the LIBPF library itself;

  • Line contains only semicolon. If this should be an empty statement, use { } instead. [whitespace/semicolon]

  • Using deprecated casting style. Use static_cast<double>(...) instead [readability/casting]

  • Is this a non-const reference? If so, make const or use a pointer. [runtime/references], except in catch(Error &e) and in print(os)

  • If an else has a brace on one side, it should have it on both [readability/braces]

  • An else should appear on the same line as the preceding } [whitespace/newline]

  • Using sizeof(type). Use sizeof(varname) instead if possible [runtime/sizeof]

  • Use int16/int64/etc, rather than the C type long [runtime/int]

  • Else clause should never be on same line as else (use 2 lines) [whitespace/newline]

  • Using C-style cast. Use static_cast<double>(...) instead [readability/casting]

  • Never use sprintf. Use snprintf instead. [runtime/printf] For Microsoft Visual studio (where C99 snprintf is not available), use the ugly trick:

    #define snprintf _snprintf
    
  • Multi-line string ("...") found. This lint script doesn't do well with such strings, and may give bogus warnings. They're ugly and unnecessary, and you should use concatenation instead". [readability/multiline_string]

Error reporting

To decide what error reporting mechanism to use in library production code, use this checklist:

  • for non-fatal errors, call the dedicated API (setError for LIBPF) so that execution continues

  • for fatal errors, due to exceptional, unlikely, or erroneous situations, but not impossible situations (in particular due to errors in calling the API from the part of the library user or the function being unable to fulfill the promise that it made in the contract to the calling code), throw exceptions; the library user can catch them and perform stack unwinding to locate the problem; it should always be possible to produce a test case which exercises a given throw statement

  • to check something that should always be true regardless of the inputs or computations performed (= sanity check), use asserts as debugging aids in developing the algorithms and to document them; it should never be possible to produce a test case which causes an assertion to trip

  • use C++11 static_assert if the condition can be evaluated and checked at compile-time (11)

  • use the assert macro from the <cassert> header, also known as a C assert or run-time assertion, to check run-time conditions and abort execution if the condition is not met

References

  1. Todd Hoff, C++ Coding Standard, 2008-03-01

  2. Geotechnical Software Services, C++ Programming Style Guidelines, Version 4.7, October 2008

  3. THE PROGRAMMING RESEARCH GROUP, High Integrity C++ Coding Standard Manual - Version 2.4, 2007

  4. Google C++ Style Guide

  5. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

  6. Dimitri van Heesch, Doxygen Manual

  7. CamelCase From Wikipedia, the free encyclopedia

  8. ISO, "Programming Language C++" ISO/IEC 14882:2011

  9. Bjarne Stroustrup "The C++ Programming Language" 4th ed. Addison-Wesley 2013

  10. Boost: Managing Warnings from Compilers (and other tools)

  11. Exceptions, error codes, and assertions in C++