Page tree

This page is still under construction ...

This page describes our conventions for code layout, name conventions, code style (bracket placement, indentation and so on), documentation, etc.

These are strictly enforced. While this may seem picky at first, the goal is to make the code easy to read and understand, which is necessary for keeping it reusable and maintainable. Code is written once by one person but read many hundreds of times by many people, so spending a little extra time while writing saves a lot of time later.

Most conventions also help to avoid bugs and enforce low-level design principles. (If you want to know the reason for a particular convention, ask Frank ...)

Code Layout

Directory Structure

The SCETlib source tree is structured as follows:

pathcontains
build*/
all build files
cmake/
cmake modules
doc/
documentation
xmath/
source tree of the X-Math package
include/scetlib/<module>/*.hpp

header files for <module>

mma/<module>
Mma interface for <module>
share/scetlib/<module>

data files for <module>

src/<module>/*.cpp

source files for <module>

testing/<module>
tests for <module>

Namespaces

  • Everything in core is in namespace scet
  • The code for each module is in namespace scet::module

  • Internals can be further encapsulated into scet::module::internal
  • All test code for a module is in scet::module::testing
  • The Mathematica interface for a module is in scet::module::mma
  • The X-Math package has its own namespace xmath

File Layout

Every high-level class has its own header file Class_name.hpp and source file Class_name.cpp. Small utility classes and aggregation structs are grouped logically into a common header file.

Headers should contain declarations only, while implementation details should go into source files. Exceptions are template and inline definitions which necessarily have to be in the header, but should then go at the end of the header.

The overall layout to follow for header and sources files is shown in doc/Foo_class.hpp and doc/Foo_class.cpp.

Header Files

Rules for #includes

  • Each header must have an include guard which is effectively the ALL_CAPS version of its full path.
  • do not use any using directives, in particular no using namespace

  • #include all necessary headers
    • Do not rely on the includes of included headers
  • #include only the minimal necessary header files needed to declare a class
    • Usually only headers for the base class and data members are needed.
    • Use forward declarations for anything only used by reference or pointer.
    • Use #include <iosfwd> to get forward declarations for the standard iostream classes.
    • You can rely on the base class to include everything needed for its public and protected interface (but not its private parts or implementation.)

The class interface is the class' table-of-contents and the primary place clients will look at. Therefore it should be accessible (i.e. clean and easily readable), expressive, and well-documented (see below for documentation rules). Users should not have to look at the source to be able to use a class.

In the class definition:

  1. Public type definitions (enums, using directives)
  2. Public member functions (the public interface)
    • First constructors and destructor
    • Then simple accessors (getters and setters)
    • Other members grouped logically
  3. Protected member functions (the interface for derived classes)
  4. Private parts (implementation details)
    • data members and member functions

Avoid inline definitions in the class interface to avoid cluttering the interface, unless for

  • trivial getters/setters
  • trivial forwarding

Source Files

  • #include everything needed for the implementation
    • Everything that was only forward-declared in the header
    • Do not rely on includes of included headers
  • #include only the minimal needed headers
    • Do not include again what was already included in the header
  • Avoid any using namespace something directives

    • For things that are heavily used, use specific directives like using std::vector
  • Implement member functions in exactly the same order as they appear in the header. Otherwise finding the implementation of a specific function quickly becomes very cumbersome.

Code Style

Naming

Consistent conventions for names of variables, functions, classes are essential to make the code readable and intuitive.

  • Names should never contain special characters.
  • Use all_lower_case_with_underscore for most names:
    • Class names start with an upper case letter: Class_name

    • Function and variable names always start lower case: function_name, variable_name
    • Utility type definitions can be lower case, in which case they must end in _t or _type
    • All nonpublic members (variables, functions, types) are lower case and start with an underscore.
    • Exceptions for capitalization are allowed only if
      • otherwise the physics meaning is completely obscured
      • and the above rules for the first letter are preserved
  • Use CamelCaseNames only in the following cases:
    • For template type names (starting with a capital letter)
    • In the Mathematica interface for names of exported functions and classes (because underscores are not allowed in Mathematica names)
    • Otherwise they are still present but deprecated and should be avoided for new code.

Choose names that are first expressive and convey information about definitions and second as short as possible. This requires striking a good balance. If in doubt, err on the side of longer more expressive names.

  • Do not use cryptic abbreviations.
  • Do use obvious shortforms to avoid very long names.
  • Avoid information that is redundant and/or obvious from the context.
  • Start nontrivial member functions with an active verb for what the function does.
  • Call booleans has_something or is_something.
Naming Examples
PDF _my_pdf;   				 // okay-ish
PDF _pdf;      				 // better, "my" is superfluous as "_" indicates a private member

double set_mu(double mu);    // okay-ish
double set_scale(double mu); // better

double return_scale();       // yuck
double get_scale();          // better, but "get" implies a nontrivial action to be performed
double scale();              // best for trivial getters

double sigma_LO;  			// not sigma_lo
double sigma_NLO; 			// not sigma_nlo

double _E;                  // strictly forbidden
double E;       			// forbidden but tolerated for local variables
                            // ("e" clearly has a different meaning)
double energy;  			// preferred, because it is more descriptive
double _energy;

int iteratorVariableForTheSecondIfStatement; // bad: unreadable, redundant
int iterator_variable       // still bad
int i, j;     				// okay for simple int iterators
iterator it;  				// okay for other iterators

int lnmbr;					// grrr
int line;                   // good
int line_number;	        // better if "line" would be ambiguous

std::vector<...> csvec;         // grrr-yptic
std::vector<...> coeffs_vector; // still not good
std::vector<...> coeffs;        // better, the _vector is redundant and includes unnecessary implementation information
							    // it may be necessary to distinguish different types of coefficients, like coeffs_list
                                // and coeffs_vector, which however is typically an indication of poor design

std::array<...> log_expansion_coefficients; // better, it conveys what the coefficients actually are
std::array<...> log_expansion_coeffs;       // better still
std::array<...> log_coeffs;                 // better if the expansion is obvious from the context

Formatting

The rules for how to format the code are illustrated in doc/Foo_class.cpp and doc/Foo_class.hpp.

Indentation

  • Always use 3 spaces (and never tabs) for indentation
    • Configure your editor so it inserts 3 spaces when you press the Tab key, not just to display tabs as 3 spaces. (In fact, while you're at it configure it so it shows tabs as 8 spaces or in some other way so you notice rogue tabs.)

  • Indent all blocks except namespaces

  • Opening braces { are on the same line for control structures if, while, switch, try, catch, etc.
  • Opening braces { are on a new line for namespaces, type definitions (class, struct, enum), and function definitions
  • Closing braces } are always on a new line.

White Space

White space enhances readability but too much is counter-productive, therefore:

  • Do not put empty lines after opening braces or before closing braces (except for namespaces)
  • Use at most 1 empty line
    • at file level between separate functions and classes
    • inside a class definition to separate functions
    • inside a function to separate logical pieces of code
  • 2 empty lines only at file level to separate
    • logically distinct classes or sets of classes
    • logically distinct sets of functions
  • No spaces
    • around function brackets, neither inside nor outside
    • for unary operators
  • Exactly 1 space
    • after control structures (if, then, else, for, etc.)
    • after commas in any type of argument lists (functions, templates, initializer lists)
    • by default around all binary operators: '=', '+=', '==', '!=', '<', '>', '||', '&&', '<<', '>>', '+', '-', etc.
    • For long equations use spaces to maximize readability. Usually spaces around '+' and - and no spaces around * and / works well.
  • No trailing spaces at end of lines and in empty lines
    • Configure your editor to show these or remove them while editing or on saving
Example
namespace scet
{                                      // opening braces for namespaces on new line

double Foo_class::member_function(double arg1, double arg2)
{                                      // opening braces for functions on new line, no additional empty line
   double a, b, c;
   a = 2.5;                            // separate statements on separate lines
   b = 2 * a;                          // spaces around operators
   b -= 2.5;
   c = ( 2.*(a + b) + (3.*a + 4.*b) ) / a;  // add/remove spaces for readability on long equations
   
   if (arg1 == arg2) {                 // space after if and opening brace for if on *same* line
      bar(arg1);                       // no space after function name
      cout << "arg1 equals arg2" << endl;  // spaces around operators
      return a * arg1;
   } else if (is_bar()) {              // else if on the same new line as closing brace of if
      another_bar(arg1 + arg2);
      return arg2;                     
   } else {
      bar(arg2);                       // use open/close braces even for single statement in long if/else
   }

   double* p;                          // pointer with type, not double *p
   if (a == b)                         // space after if again
      p = &a;                          // can avoid braces for single line if/else
   else
      p = &b;
   
   for (int i = 0; i < 10; ++i) {      // space after for and between control statements, opening brace on same line
      ...
   }                                   // closing brace on new line 
   
   int foo = get_foo(a, b);
   // TODO: improve efficiency         // in in-code comments use TODO, HACK, BUG, WARNING, NOTE as needed   
   switch (foo) {
      case 1:
         a += 1.;
         break;
      case 2:
         a += 2.;
         break;
      default:
         a += 3.;
   }
   
   while (foo != 0) {
      b += *p;
      --foo;
   }
   
   return 2.0;
}                                      // closing braces for functions on new line

} // namespace scet

Code Documentation

... to be continued

 

 

Page Contents


Subpages

 

Create new subpage

 

 

  • No labels