CCP4i Developer's Style Guide

Peter Briggs
Draft version 05/05/2005, 20/01/2005, 19/01/2005

1. Introduction: why have a programmer's style guide?

A style guide sets out conventions for code, which are useful for the following reasons:

This style guide sets out conventions for writing CCP4i code. I have based this guide on two documents on the web which give general Tcl coding conventions, and these are worth examining in their own right:

Note that these are not entirely consistent. However there is a lot of useful advice here.

In addition I have added CCP4i-specific conventions based on my experience and on comments received from the original CCP4i developer.

2. General Tcl coding style

2.1 Low-level coding conventions/formatting

Generally one should aim to be consistent with the style of existing CCP4i.

2.1.1 Indentation and line breaks

My preferred indentation style is to indent each level by 2 spaces. "Long" lines should be broken at 80 characters and the continuation mark "\" should be used. Continued lines should be indented.

2.1.2 Only one command per line

You should only have one Tcl command per line on the page. Do not use the the semi-colon character to place multiple commands on the same line. This makes the code easier to read and helps with debugging.

2.1.3 Curly braces: { goes at the end of a line

Open curly braces should not appear on lines by themselves, instead they should be placed at the end of the preceeding line. Close curly braces are indented to the same level as the outer code.

Control structures should always use curly braces, even if there is only one statement in the block:

if { $tcl_platform(platform) == "unix" } {
  return
}

2.1.4 Declare globals and upvars at the start of procedures

So e.g.

proc myProc { var y } {
  ...Some code...
  upvar x $var
  set x $y
  ...More code...
  global stuff
  set stuff 1
  ...Even more code...
}

is deprecated. Instead use

proc myProc { var y } {
  global stuff
  upvar x $var
  set x $y
  set stuff 1
  ...Rest of the code...
}

This makes it easier to see the scope of imported variables within the procedure.

2.1.5 Always use the return statement

You should always explicitly use the return statement to return values from a Tcl procedure. By default Tcl will return the value of the last Tcl statement executed in a Tcl procedure as the return value of the procedure (this can lead to confusion as to where the result is coming from).

You should use a return statement with no argument for procedures whose results are ignored.

2.1.6 Switch statements

The switch statement should be formatted as below:

switch -regexp -- $string {
  plus -
  add {
    ...
  }
  subtract {
    ...
  }
  default {
    ...
  }
}

Use the -- option to avoid having the string be confused with an option (this can happen if the string is user generated). (More generally -- should be used wherever available e.g. in regexp, for the same reasons.)

2.1.7 If statements

Avoid using the then word of an if statement, however always use the else word (as it imparts some semantic information), e.g.:

if { $x < 0 } {
  ...
} elseif { $x == 0 } {
  ...
} else {
  ...
}

2.2 Naming Conventions

2.2.1 Command and procedure names

Procedures which are captialised without spaces (e.g. "GetTypeInfo") are generally API commands. Procedures which are lowercased and underline-separated (e.g. "get_type_list") are generally internal procedures which ideally shouldn't be used directly by an application programmer.

Note that this makes sense in the context of core CCP4i commands (i.e. those in the files in src and utils) but less so for task files (i.e. those in the tasks directory). If there is a system then it is that procedures which are called by the 'interpreter' (e.g. are used to draw one line of extending frames) are regarded as public and any additional utilities are private. Note that this is not strongly adhered to in practice.

Procedures in task interfaces should be prepended with the name of the task e.g. ScalaCheckDatasets or scala_get_column_id. (This implements a type of weak namespacing.)

2.2.2 Varible and argument names

A variable whose value refers to another variable has a name that ends in Var, e.g. dataVar.

3. CCP4i-specific coding style

3.1 Conventions for def files

3.1.1 General naming considerations

Parameters in def files should always be uppercased and underscore-separated, e.g. ANOMALOUS or RUN_SORTMTZ.

Parameters which are used as counting variables, for example counting the number of lines in an extending frame, should start with N or N_, e.g. NRUNS or N_SCALA_HKLOUT.

Parameter names should not contain commas "," as these denote indexed parameters (see below).

3.1.2 Indexed parameters

Declaring a parameter is an "array" (i.e. indexed) parameter requires that the zero-th indexed element is declared in the def file. This ends in ,0 e.g. MR_ENSEMBLE_ID,0. Failing to do this may generate errors of the form:

can't set "MR_ENSEMBLE_ID(0)": variable isn't array
when starting CCP4i. Note that for parameters with two indices (e.g. "X,1_1") the initialisation still only requires "X,0" to be defined in the def file i.e. you don't need "X,0_0" defined.

Indexed parameters start counting from element 1. The type and value assigned to "X,0" in the def file forms the defaults which are used when new instances of the indexed parameter are generated.

The CCP4i commands Indx, GetIndx and Indxv0 are used with indexed parameters:

These commands should be used when handling indexed parameters (rather than constructing the indices explicitly).

3.1.3 Implicit parameter names

For file name parameters: where FILENAME indicates a parameter with the name of a file, there should be a corresponding parameter DIR_FILENAME (of type "_default_dirs") in the def file. Many CCP4i functions implicitly assume that this second parameter exists, for example CreateInputFileLine.

3.1.4 Administrative parameters and reserved prefixes

CCP4i creates a number of internal administrative variables within its arrays. These parameters are distinguished by being prepended with one of a number of reserved prefixes:

WIDGET_*
LABEL_*
XF_*
HELP_*
FRAME_*
DEPVARS_*
N_DEPFRAMES_*
DEPFRAMES_*
CHILD_*
PARENT_*
VARS_*

Programmers should avoid explicitly defining parameters of this form within the def files.

CCP4i v1.4.0 has a procedure called reserved_prefix that checks whether a parameter name has one of the above administrative prefixes.

3.1.5 Formatting def files

Def files can be in one of two formats. The first has three fields per line specifying parameter name, parameter type and parameter value. The second has two fields per line specifying parameter name and parameter value. In each format the fields are separated by spaces.

Each line should be spaced to align the fields into columns as this makes it much easier to read.

Comment lines are indicated by the presence of a hash character "#" at the start of the line. Comment lines starting with #CCP4I have special significance for CCP4i as these encode internal information as name-value pairs which may be used elsewhere.

Distributed def files should also include the appropriate copyright notice and a CVS id string:

#CCP4i_cvs_Id $Id: $

3.2 Task and source code (.tcl) files

Each file should have the appropriate copyright notice at the top, followed by a CVS id string:

#CCP4i_cvs_Id $Id: $

4. Where to place functionality

Some scripts source extra code (e.g from CCP4_utils.tcl). The original idea was that code in src was not used by the scripts and any general utilities that the scripts might also use went in the utils directory.

5. Common Constructs

5.1 Querying the operating system type

The global array system contains an element OPSYS:

set system(OPSYS) [string toupper $tcl_platform(platform)]

This is used to distinguish between Unix (UNIX) and Windows-based (WINDOWS) systems.

6. Documentation

The CCP4i programmers documentation includes a section on how to document the CCP4i commands using doc comments: go to "Programmers Documentation" and then scroll down to "How to document core CCP4i Tcl Procedures". Alternatively it will be located at $CCP4/ccp4i/help/programmers/documenting.html on the local system.

7. Tools

Chris Morris has suggested that in some opinions a style guide should only contain things which can be checked automatically by a software tool. He has suggested the TclPro Checker as a possible tool for this: