SST Coding Standards

Introduction

The SST project receives contributions from a large number of developers, many with different local coding standards. In order to develop readable, maintainable code, all code contributed to the SST project should follow these coding standards. Libraries which are not part of SST but are wrapped in SST elements, such as the gem5 simulation environment, are not expected to follow these standards. However, the wrapper elements, such as the m5C element library, are expected to follow these standards.

NOTE Much of the code in SST pre-dates these coding standards. While it is expected that new code will follow these standards, it may take some time for all code to conform.

Changing SST

In general, developers should feel free to commit changes to SST directly to the trunk, as long as the changes conform to these coding standards. Large or highly impactful changes should be discussed on the SST developers mailing list before committing, to avoid duplication of effort or adverse interaction between different change sets. Note that adding an element library (or an element to an existing element library) is not generally considered a larger or impactful change.

Core

Stability is a primary concern for the SST core library. Interface changes or deletions should be undertaken only with discussion of the SST community and only during major release cycles. Backwards compatible interface additions should be discussed prior to committing to the repository, as they limit future changes.

Unlike elements in element libraries (i.e., extensions to SST), the core should be documented using Doxygen. Before committing, developers should ensure that the Doxygen documentation builds properly.

Elements / Element Libraries

Because elements are relatively self-contained, API stability is not a primary concern. However, events which are used by other elements (such as the memory events associated with the memory hierarchy element) should be changed only when absolutely necessary. Each element must follow constraints documented in the Element Library Documentation

Code Format

Indentation

1. Use spaces, not tabs. Tabs should only appear in files that require them, such as Makefiles. 2. Indents should be 4 spaces 3. A case label should line up with its switch statement. The case body is indented. For example:

switch (condition) {
case A:
case B:
    i++;
    break;
default:
    break;
}

4. Boolean expressions at the same nesting level which span multiple lines should have their operators on the right side of the line successive lines should be vertically aligned. For example:

if (a == b ||
    c == d) {
    return true;
}

Spacing

1. Spaces should be placed around binary and ternary operators. For example:

a = b * c / d;
func(e, f, g, h);
i = j & k;
return cond : 1 : 0;

2. Spaces should not be inserted around unary operators. For example:

i++;

Not:

i ++;

3. Spaces should not be placed before commas or semicolons:

for (int i = 0; i < 10; ++i) {
    compute(i);
}
func(a, b);

4. Spaces should be added between control statements and parenthesis:

if (a) return true;
for (int i = 0; i < 10; ++i) {
    compute(i);
}

5. Do not place spaces between a function and its parenthesis or between parenthesis and content:

func(a, b, c);

Line Breaks

1. In general, each statement should get its own line. However, for simple conditionals with a single statement body and no else clause, a single line may be used:

x++;
func(a, b, c);
if (cond) return false;

2. Conditional bodies should be enclosed in braces (unless simple, as described in the previous rule). else statements should go on the same line as the preceding close brace:

if (cond) {
    return true;
} else {
    return false;
}

3. Return types (and function type modifiers such as inline and static) should be on the same line as the function declaration / definition.

Braces

1. Function definitions: place each brace on its own line:

int func(int a, int b, int c)
{
    ...
}

2. Other braces: place the open brace on the line preceding the code block and place the close brace on its own line. An else/else if clause should be placed on the same line as the close brace for the preceding block:

class MyClass {
    ....
}
for (int i = 0; i < 10; ++i) {
    ...
}
if (condA) {
    ...
} else if (condB) {
    ...
} else {
    ...
}

3. Control clauses without a body should use empty braces:

for ( ; NULL != ptr; ptr = ptr->next) { }

Null and Boolean

1. The null pointer value should be written as NULL. 2. bool values should be written as true and false.

Conditionals

1. Tests for true/false should be done without equality comparisons:

if (condition) return true;

2. Tests for null/non-null and zero/non-zero should be explicit:

if (NULL != ptr) return;
if (0 == value) return;

3. When comparing against a constant, the constant should be the first operand:

if (NULL != ptr) return;

Names

1. Getters and setters in C++ classes should be uniformly named to be getFOO and setFOO, with appropriate use of const:

std::string getFoo() const;
void setFoo(std::string const& val);

2. Variable, class, and function names should be descriptive, even if that results in a longer name. 3. Function names should start with a lower case letter and then use CamelCase (not underscores). 4. Class names should start with an upper case letter and then use CamelCase (not underscores).

Pointers and References

Header Files

  1. All implementation files must #include "sst_config.h" before any other statements. Header files should never include set_config.h.
  2. All implementation files must #include their associated header file second, just after sst_config.h. For example, factory.c must include factory.h before any other headers. This helps ensure that each header file can stand on its own without silent dependencies.
  3. After the config file and primary header, headers should be included in three sections and ordered alphabetical within a section:
    1. system headers
    2. core headers
    3. element library headers (if in an element library)
  4. Headers should only include other headers necessary for the compilation of that header. They should not include headers only necessary for the compilation of the implementation file.
  5. Whenever possible, a header should forward declare a class rather than include the header for that class see cplusplus article.
  6. Core header files should provide multiple-include protection with a define name of the format SST_CORE_<header>_H. There should not be proceeding and trailing includes.
  7. Element header files should provide multiple-include protection with a define name of the format SST_<element_library_name>_<element_name>_H.

Comments

1. Prefer comments above a statement instead of after the statement:

// Only create an initialization checkpoint if explicitly requested.
if (checkpoint_enabled) checkpoint();

2. Non-trivial functions should be preceded with a comment block explaining the purpose of the function.

// clock tick function.  Progresses the state machine one cycle.
state_machine_cpu::tick(...)
{
    ...
}

state_machine_cpu::isDone()
{
    return (cycle < max_cycle) ? false : true;
}

3. Comments should be in sentence form. 4. Use FIXME: without attribution to denote items which should be fixed.

abort(); // FIXME: The code should gracefully clean up and end simulation here.

5. All non-file local functions in the core should be commented with Doxygen comments. Doxygen comments should use the /** notation.

Serialization

  1. When exporting classes for serialization, Use the fully qualified namespace to the object. This eliminates the possibility of duplicated object names being used by Boost serialization.
BOOST_CLASS_EXPORT(SST::SimpleComponent::simpleComponentEvent)

Output

Compilation

  1. All code should be warning free on a developer build (i.e., with developer warnings enabled) with a modern version of the GCC suite.
  2. Code should avoid non-portable constructs such as __attribute__ or ensure that proper compiler guards are in place for compilers which do not support that feature. Non-portable features should not be used to silence warnings; instead the code should be refactored to avoid the warning in the first place.