This project adheres to the following coding styles:
- Google C++ Style Guide (GCSG)
- Google Python Style Guide (GPSG)
- PEP 8 -- Style Guide for Python Code (PEP8)
- KDE CMake Coding Style
- Qt Coding Style, Qt Creator Coding Rules for the GUI Development
- Google Shell Style Guide
- Use
pragma once
instead of header guards. - Exceptions are allowed.
- Name mutator functions without
set_
prefix. - Multiple implementation inheritance is allowed (mostly for mixins).
- One-liner
if statements
are not allowed. It interferes with interactive debugging and code coverage analysis tools (may hide uncovered lines).
80-character line length limit (vs. 100).
Exceptions are allowed.
using-directives
are forbidden.Prefer anonymous
namespace
tostatic
keyword.The GCSG style
include
of headers:Sections:
- Related header (including
ui
header) - C-system headers
- C++ system headers
- Qt headers
- Other libraries' headers
- Project Core headers
- Project GUI headers
- Related header (including
Sections are grouped by a blank line.
Within each section the includes are ordered alphabetically.
The GCSG-style declaration order in class definition.
Use
nullptr
instead of literal0
orNULL
.
Use modern C++ (C++17). Refer to C++ Core Guidelines for best practices.
Do not use
inline
when defining a function in a class definition. It is implicitlyinline
. Do not useinline
as an optimization hint for the compiler. Only reasonable use ofinline
is for the linker to avoid violating the ODR.!bool
vs.bool == false
- Prefer using the negation and implicit conversions to
bool
only with Boolean or Boolean-like values (bool
,nullptr
,0
for non-existence, etc.). An example abuse of the negation and implicit conversion tobool
would be:
double checked_div(double x, double y) { if (!y) // Bad. Looks like 'if (no y) then fail'. throw domain_error(""); // More explicit (y == 0) is better. return x / y; }
- However, if the expression is long (3 or more parts),
prefer comparing with the value (
false
,0
, etc.) explicitly for readability:
if (var.getter().data().empty() == false);
- Avoid inverted or negated logic if possible.
- Prefer using the negation and implicit conversions to
Prefer explicit function to pointer conversion with operator
&
instead of implicit decay.Prefer implicit dereference in a function call through a pointer.
Exceptions are forbidden in analysis code.
RTTI (typeid, dynamic_cast, dynamic_pointer_cast, etc.) is forbidden in analysis code.
Defensive Programming. Check all preconditions, postconditions, invariants, and assumptions with the
assert
macro wherever possible in analysis code. Consider supplying an error message to clarify the assertion, for example,assert(!node->mark() && "Detected a cycle!")
.If function input parameters or return values are pointers (raw or smart), they are never null pointers unless explicitly specified. Null-based logic must be rare, localized, and explicit (consider using
std::optional
instead).Consider supplying a typedef or alias declaration for common smart pointers.
ClassNamePtr
for shared, unique, and intrusive pointersClassNameWeakPtr
for weak pointers
Function call qualification conventions:
Unqualified calls customizable by or relying on the ADL must make it explicit in the documentation and comments.
In definitions of member functions:
- Explicitly qualify calls to inherited non-virtual member functions
with the corresponding base class names, e.g.,
BaseClassName::Foo()
. - Qualify virtual functions to be overridden by design as
this->Foo()
. - Qualify a call to a free function with its namespace, e.g.,
scram::Foo()
.
- Explicitly qualify calls to inherited non-virtual member functions
with the corresponding base class names, e.g.,
In definitions of free functions, calls to other free functions in the enclosing namespace can be unqualified.
Declare a getter function before a setter function for a corresponding member variable.
Declare getter and setter functions before other complex member functions.
Domain-specific
Probability
naming rules:If a probability variable is a member variable of a class, abbreviate it to
p_
. Its getter/setter functions should have corresponding names, i.e.,p()
andp(double value)
. Append extra description afterp_
, e.g.,p_total_
(a la Semantic Hungarian). Avoid abbreviating the name toprob
or fully spelling it toprobability
.For non-member probability variables:
- Prefer prefixing with
p_
(a la Semantic Hungarian) if the name has more description to the probability value, e.g.,p_not_event
. - Prefer
prob
abbreviation for single word names indicating general probability values.
- Prefer prefixing with
Prefer spelling
Probability
fully for cases not covered above (class/function/namespace/typedef/...), e.g.,CalculateProbability
. Avoid abbreviating the name, e.g.,CalculateProb
.
In analysis code, prefer the terminology and concepts of Boolean algebra, graph theory, sets (i.e., the solution domain) to the terminology and concepts of risk analysis (i.e., the problem domain).
In performance-critical analysis code (BDD variable ordering, Boolean formula rewriting/preprocessing, etc.), avoid platform/implementation-dependent constructs (iterating over unordered containers, unstable sorts, using an object address as its identity, etc.). The performance profile must be stable across platforms.
- Avoid Qt containers whenever possible. Prefer STL/Boost containers and constructs.
- Upon using Qt containers and constructs, stick to their STL API and usage style as much as possible. Avoid the Java-style API.
- Upon using Qt specialized containers (e.g.,
QStringList
), do not use a single-element constructor (e.g.,QStringList(QString)
). Use the initializer list instead. - Avoid default arguments in signals and slots.
- Prefer Qt Designer UI forms over hand-coded GUI.
- Common Qt includes may be omitted,
for example,
QString
,QList
,QStringList
, andQDir
. - Avoid using forward declaration of Qt library classes. Just include the needed headers.
- Avoid
qobject_cast
and its flavors. Avoid the RTTI in general. - Automatic (implicit) connection of signals and slots is forbidden.
- Avoid using unqualified
tr()
calls. Qt Linguist automatic context deduction is flaky and often fails with modern C++ code._()
(a-lagettext
) is provided to always resolve the context toQObject::tr()
(the common case). Add extra translation/disambiguation context as needed (the rare case).
- Performance profiling with Gprof, Valgrind, and
perf
- Code coverage check with Gcov and reporting with Codecov
- Memory management bugs and leaks with Valgrind
- Static code analysis with Coverity and CppCheck
- Cyclomatic complexity analysis with Lizard
- Google style conformance check with Cpplint
- Common C++ code problem check with cppclean
- Consistent code formatting with ClangFormat
- Component dependency analysis with cppdep
- Code quality and style check with Pylint
- Profiling with PyVmMonitor
- Code coverage check with coverage and reporting with Codecov
- Continuous code quality control on Codacy with Prospector
- Consistent code formatting with YAPF
Metric | Before Release | On Release |
---|---|---|
C++ Code Coverage | 80% | 95% |
C++ Defect Density | 0.5 per 1000 SLOC | 0.35 per 1000 SLOC |
CCN | 15 | 15 |
Python Code Coverage | 80% | 95% |
Pylint Score | 9.0 | 9.5 |
Documentation | Full | Full |
Note
C++ defects that count towards the defect density include analysis errors, Coverity report, memory leaks, and known critical bugs.
Note
Utility scripts written in Python are exempt from the test coverage requirement.
In order to facilitate better software quality and quality assurance, full test coverage is attempted through unit, integration, regression, and benchmarking tests. The following tools are used for this purpose:
These tests are automated, and continuous integration is provided by Travis CI and AppVeyor.
Guided fuzz testing is performed with auto-generated analysis input files to discover bugs, bottlenecks, and assumption failures.
- Writing Unit Tests (Qt GUI)
- Software Testing Fundamentals
- Software Testing Tutorial
- ISO Standards for Software Testing
- Introduction to Test Driven Development
- Git SCM
- Branching Model
- Writing Good Commit Messages
- On Commit Messages
- Atomic Commit
- Semantic Versioning
Good documentation of the code and functionality is the requirement for maintainability and evolution of the project.
The project adheres to the Documentation Driven Development model (DDD talk by Corey Oordt), following the best practices of Agile Documentation, Google Documentation Guide Philosophy and Best Practices.
The documentation for the project is maintained in the reStructuredText format, and the final representations are dynamically generated with Sphinx in various formats (html, pdf, LaTeX).
The code documentation is dynamically generated with Doxygen, which also verifies full documentation coverage.
The source text of the documentation in the code and the reST format must be formatted consistently and with Semantic Linefeeds for maintainability and version control.
- Prefer the :ref:`Aralia_format` for the Boolean formula documentation. This format uses the C-style bit-wise logical operators for formulas.
- Semantic Linefeeds
- Two blank lines between sections with bodies
- One blank line after a header before its body
- Part
#
overlined and underlined - Chapter
*
overlined and underlined - Section underlining and order
=
,-
,~
,^
,+
- Point nesting and order
-
,*
,+
- 4-space indentation
- 100 character line limit (except for links and paths)
- No trailing whitespace characters
- No tabs (spaces only)
- No excessive blank lines at the end of files
Semantic Linefeeds
Doxygen comments with
///
and///<
Comment ordering:
- description
- tparam
- param
- returns
- pre
- post
- throws
- note
- warning
- todo
Leave one Doxygen blank line between sections
Always specify input and output parameters with
@param[in,out] arg Description...
- Two spaces between parameter and its description
- The same formatting for template parameters
@tparam T Type desc...
The two-space formatting for
@throws Error Description
In-code TODOs with Doxygen
/// @todo
so that Doxygen picks them up.
- 2-space indentation
- No tabs (spaces only)
- No trailing whitespace characters
- No excessive blank lines
- No spaces around tag opening and closing brackets:
<
,/>
,<\
,>
. - Only one space between attributes
- No spaces around
=
in attribute value assignment - Prefer 100 character line limit
- Avoid putting several elements on the same line
- UTF-8 encoding