Skip to content

Code formatting recommendations

Max Horn edited this page Jan 22, 2017 · 7 revisions

This file contains recommendations on how code contributed to GAP itself (kernel and library) should be formatted.

Preamble: Why code formatting recommendations?

Formatting code in a clean, readable way is an important for code hygiene, and as such a well-established norm for any larger software project, such as GAP. To whit, most of these projects recommend or even rigorously enforce code formatting guidelines. A few examples:

While we do not wish to force any specific formatting rules on anybody, we nevertheless reserve the liberty of rejecting code contributions for GAP which are formatted in a fashion that makes understanding the code difficult. We may then point potential contributors to this guideline to help them get an idea of what we consider good formatting.

That said, in the end, common sense has precedence -- this is not about counting individual spaces, but about readability and internal consistency of code.

Beyond that, for those of us who wish to write code in a globally consistent way, this guide helps us to coordinate by making some specific suggestions as to how to format specific kinds of code. These are not binding, but we encourage everybody to consider following them, even package authors.

When and how to reformat my code?

  • Do not reformat big bunches of existing code in the GAP codebase right now!
    • We may eventually do this, but for now, only new code contribution should be (re)formatted according to these rules. Any such reformatting complicates merges between master and hpcgap-default, and makes the output of git annotate less useful. * Never make mix reformatting and logic changes
    • If you fix a bug or add a feature, and at the same time (in the same commit) reformat the code, then this makes it hard to spot what you actually did by looking at the diff. Thus, it makes code review very hard if not impossible.
  • When editing an existing function, always try to match the surrounding code.
    • So if the function uses 2-space indents, do that, too.
    • Rationale: Code can be very confusing if in the middle of it the formatting changes - some of the worst examples mix tabs and spaces, leading to a result that is hard to read with any tab setting. Please avoid that.
  • For GAP code, you can use Display(func); to let GAP format your function, to get a basic idea how it should look.
    • Unfortunately, this strips comments, so for now this is of limited use. We hope to be able to provide a proper code reformatter for GAP code in the future
  • For C code, clang-format is a great formatting tool
    • We will provide a settings file for it which respects the recommendations described below, and which you can use to format a function or a whole file
    • But remember, for now this should be limited to new code only.

TODO

General remarks

The following are for both kernel (C) and library (GAP) code:

  • never use tabs to indent code! Use simple whitespace instead.
    • different people use different tab settings; 4 and 8 are common, but also 2, 3 and others are not unheard of. As a result, people have to adjust their tab settings to read your code. And if the next function was written by somebody with another tab setting, one has to adjust the tab settings again as one switches between the two function
  • NEVER EVER mix tabs and spaces.
    • This results in the worst of all situations.
    • Unfortunately, at least on popular editor (emacs) uses this as a default setting. Please turn it of.
  • we recommend 4 spaces per indention level
    • a frequently seen alternative in existing code is to use 2 spaces, which is also fine.
    • but please do not use anything other than 2 or 4
  • maximal line length: 78 (with exceptions possible in justified cases; use common sense)
    • historically, this is to accommodate people editing code on terminals which are limited to 80 characters (minus 2 to accommodate for e.g. scrollbars)
    • while terminal may not be relevant anymore, overlong lines still make reading harder (this is why newspapers use columns: wide text is difficult to parse correctly for humans)
    • that said, if your code is indented deeply, it may certainly be OK to break this rule, but please think about it first
  • avoid trailing spaces on lines
  • end your files with a single newline

C code formatting

General

  • Use common sense above all :-)

  • Use hugging braces (preserves vertical screen space). But place else on new line, to make it easy to rearrange, remove or add else/else if blocks.

// good
int f() {
  if (cond) {
     statement;
  }
  else if (otherCond) {
     statement;
  }
  else {
     statement;
  }
}

// bad: opening braces on new line; braces indented differently than code before and after.
int f()
{
  if (cond)
  {
     statement;
  }
  else
   {
     statement;
   }
}
  • separate reserved keywords from opening parentheses by a single whitespace

    • good: while (1) {
    • bad: while(1) {
  • separate parentheses and curly braces by a single whitespace

    • good: if (cond) {
    • bad if (cond){
  • follow commas by a single whitespace

    • good: int x, y, z or f(a, b, c)
    • bad: int x,y,z or f(a, b,c)
  • follow semicolons by a single whitespace, if there is more after it on the line

    • for (int a = 0; b < c; d++)
    • doSomething(e); doSomething(f); // This is probably bad style anyway
  • no inner bracket padding

    • good: f(a + b), if (cond)
    • bad f( a + b ), if ( cond )
  • surround binary operators by spaces

    • good: x = 1 + (a * b);
    • bad: x=1+(a*b);
  • no space between unary operators and their arguments

    • good: *x = -z + &y + 1
    • bad: * x = - z + & y + 1
  • pointer declarations: * should hug the variable name and be separate from the type name

    • good: foo(int *x) or int *x, y;
    • bad: foo(int* x) or int* x, y;
  • do not put whitespace after a cast

    • good: const char *ptr = (const char *)foobar;
    • bad: const char *ptr = (const char *) foobar;

Comments

  • Do not fill up comments with pointless spaces

    • good: /* special case for the empty blist */
    • bad: /* special case for the empty blist */
  • Both single-line // ... and multi-line comments /* ... */ are allowed. For comments that really are just on a single line, using // ... is slightly preferred.

Specific constructs / keywords

  • switch / case: (TODO: finalize this: indent case or not???)
switch (cond) {
case 1:
    statement;
    break;
default:
    statement;
    break;
}

switch (cond) {
case 1: statement; break;
default: statement; break;
}
  • for loops
for (int i = 0; i < 10; ++i) {
    statement;
}
  • for and while loops with empty loop body: use braces
  // good
  while (cond) { }
  
  // bad
  while (cond);

C99

Usage of certain C99 extensions is allowed (please ask if in doubt).

  • declare variables anywhere you like, even in for loop condition
int x;
statement;
int y;
for (int i = 0; i < 10; ++i) {
    statement;
}
  • use of single line // comments is allowed

  • Things defined in inttypes.h respectively `stdint.h may be used

  • ... ?

GAP code formatting

This should essentially match the way GAP prints functions.

This means in general it is similar to the way our C code is formatted, with some notable exceptions:

  • inner bracket padding is allowed, especially if it improves readability
  • ...

TODO: make sure GAP prints functions the way we want ;-), see also https://github.com/gap-system/gap/issues/473

InstallMethod

Calls to InstallMethod should be formatted as follows: Both the name of the operation, and the info string, should be put on the first line, to help people who want to grep for method installations. Other parameters can be spilled on subsequent lines, and they are indented by one level. The exception is the final parameter with the function to be installed. If it is a full-blown function(...) ... end construct, it should start on indention level 0. If it is a lambda or a function name, treat it like as other parameters.

# default: a full blown function, which starts at indention level 0
InstallMethod( SomeOperation, "for blue objects",
    [ IsObject ],
    1,
function( obj )
    if not IsGroup( obj ) then
        return 0;
    else
        return 42
    fi;
end );

# if the function is a lambda, or the name of an existing function,
# then put it at indention level 1:
InstallMethod( SomeOperation, "for blue objects",
    [ IsObject ],
    1,
    obj -> 2 * obj );

# You do not have to spill the extra parameters to new lines
InstallMethod( SomeOperation, "for blue objects",
    [ IsObject ], 1, SomeOtherFunction );

GAP name conventions

The GAP manual describes the conventions for GAP names, which, if used consistently, make it very easy to guess the (order of) parameters of operations. Please try to follow them. Read up on them in Chapter 5 of the GAP reference manual.

Miscellaneous

clang-format

The following is .clang-format file I once created to reformat the GAP C code. I have not verified whether it matches the above 100%, but it should be at least reasonably close and thus provide a good starting point.

# For information on the settings in this file, please consult
# http://clang.llvm.org/docs/ClangFormatStyleOptions.html


# Basic indention settings
UseTab: Never
IndentWidth: 4
ColumnLimit: 78
MaxEmptyLinesToKeep: 2

BreakBeforeBraces: Attach

Cpp11BracedListStyle: false

AlignTrailingComments: true
SpacesBeforeTrailingComments: 4

AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false

BinPackParameters: false
PointerBindsToType: false

EditorConfig

Here is a simple .editorconfig file (see http://EditorConfig.org)

# See http://EditorConfig.org

# This is the top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file.
# But do not trim trailing whitespaces -- while we
# want that eventually, it just causes pain right now,
# if it pollutes commits.
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = false
indent_style = space
indent_size = 4
charset = utf-8

# In Makefile one must use tab indentation
[Makefile*]
indent_style = tab

TODO: provide default settings for other editors

Provide settings for emacs, vim and other popular editors which make it convenient for people to use our style.