Introduction

Identification

This document is the IDL (Interactive Data Language) Coding Style Guide for Custom Visuals

Copyright © Custom Visuals, 1999.

Scope

This guide applies to the development of new software using Research Systems Inc.'s IDL language to ensure consistent coding conventions between programmers and projects. Reasons for application of conventions include maintenance and the application of automated tools. The specific application of this guide is the responsibility of the cognizant project engineer and/or program manager.

Purpose and Objectives

The conventions specified herein define the standard for all IDL code and its internal documentation. The conventions are intended to encourage higher quality code and lower maintenance requirements. Every source module is required to have certain essential documentation, and the code and documentation are required to be in a format that has been found to be readable and accessible. Since IDL distinguishes between Procedures and Functions, and this document is broad in scope, the term Programs will be used to refer to statements which can be applied to both Procedures and Functions.

The conventions are also intended to provide a level of uniformity in the code produced by different programmers. Such uniformity allows programmers to work on code written by others with less overhead in adjusting to stylistic differences. It also allows automated processing of the source.

This Standards Document is designed to be the basis for the standards on individual projects. Any specific project may install their own extensions and deviations to any of the conventions. If they do so, they are, at the very least, required to preface their standards document with the rules they have followed in building their document. That is, every project should have a standards document that references this document and then details what additional project-specific standards are required. It is assumed that projects will build on these conventions and few, if any, exceptions will be made to the conventions. As an alternative to the deviation technique detailed here, a project may produce a separate standards document for that particular project, which would incorporate these conventions. However, this later technique is discouraged.

The conventions are not intended to discourage or inhibit programmer creativity. Any coding technique that is not restricted by this document shall be considered permissible.

Finally, the conventions are designed to provide maximum portability of the code to possible future platforms and extensions to the language.

These conventions are divided into the following major categories:

Style Guide Status

This is Version 3.0 of the guide. Revisions to this document may be made at any time.

Module Composition

A module is defined as one or more related programs (and possibly data) that reside in a single source file. If the module contains module-scope data (that is, data that is accessible to all or some of the programs in the module), then any program that directly accesses any part of that data must, by necessity, be included in the module. Other than this instance, there is no hard-and-fast rule that can be used to determine how programs should be grouped into modules. The important consideration is that they must be related in some logical way. Here are some of the ways they can be related:

Module Layout

The conventions in this section define the standard layout for every source module.

Each module shall be preceded by a prologue containing documentation. The module prologue shall consist of blocks described within this section category. One or more dashed lines shall separate these blocks, and there shall be no blank lines within each block. Each line of the prologue shall be preceded with comment delimiters. The first prologue line shall be the first non-blank line in the file. A sample module is shown below:

;---------------------------------------------------------------------------
;                Copyright 1998 - Custom Visuals
;---------------------------------------------------------------------------
; Author: Mike Schienle
; $Workfile: fontgen.pro $
; $Revision: 1.1.1.1 $
; Orig Date: 96-12-17
; $Modtime: Wed Oct 01 10:26:18 1997 $
;---------------------------------------------------------------------------
; Module: FontGen
;
; Purpose: Create a default font structure.
;
; Functions: FontGen
;
; Procedures: None
;
; Calling Sequence: mFont = FontGen([Size=aSize])
;   Size is an option array of font sizes.
;
; Inputs: None required.
;
; Outputs: A structure of fonts and sizes.
;
; Keywords: Size, an integer array of font sizes.
;
; History:
;   97-10-01 MGS
;     Added SIZE keyword option.
;---------------------------------------------------------------------------
 
FUNCTION FontGen, Size=aFontSize
    ; check to see if Size keyword was specified
    IF (N_Elements(aFontSize) EQ 0L) THEN $
        ; no size provided, create an array of string sizes
        asFontSize = ['10', '12', '18', '24'] $
    ELSE $
        ; convert size array to string
        asFontSize = StrTrim(aFontSize, 2)
   
    ; initiate the mFont structure
    mFont = {init: 0}
   
    ; loop through the array elements
    FOR iType = 0, (N_Elements(asFontType) - 1) DO $
        mFont = Create_Struct(mFont, asFontType(iType))
   
    ; return the font structure
    Return, mFont
END

Configuration Management Header

A configuration management header consistent with the chosen configuration management system (i.e. RCS, CVS, SCCS, etc.) syntax shall be used. The configuration management header labels should include as a minimum the author, revision number, document name and time of last modification to the document. Configuration management keywords may be used for each of these parameters. A copyright notice is recommended.

It is strongly recommended that a program in every module be a program that unit-tests the other programs in the module. It is suggested the test program be implemented by the use of a TEST keyword.

Module Header Block

The module header block contains descriptions of the module and its associated parameters and history.

Module

The Module line shall consist of one line with the name of the module. The module name must be the same as the module file name.

Purpose

The Purpose line shall consist of one or more lines describing the purpose of the module.

Functions

The Functions line shall consist of one line per function name within the module, followed by a brief description of the function. If no functions are present within the module, the word None shall be listed.

Procedures

The Procedures line shall consist of one line per procedure name within the module, followed by a brief description of the procedure. If no procedures are present within the module, the word None shall be listed.

Calling Sequence

The Calling Sequence line shall contain the methods in which the module can be called from other programs. Optional arguments shall be described along with any necesssary data types.

Inputs

The Inputs line shall consist of one parameter per line. Each parameter shall be followed by the purpose and description of the parameter, as well as the data type (array, integer, various, etc.) of the parameter. If no parameters are allowed as input, the word None shall be listed.

If a variable is provided as an input and is changed within the module, the variable shall also be listed in the "Outputs" section.

Outputs

The Outputs line shall consist of one parameter per line. Each parameter shall be followed by the purpose and description of the parameter, as well as the data type (array, integer, various, etc.) of the parameter. If no parameters are allowed as output, the word None shall be listed.

If a variable is provided as an input and is changed within the module, the variable shall also be listed in the "Outputs" section.

Keywords

The keywords line shall consist of one parameter per line. Each parameter shall be followed by the purpose and description of the parameter, as well as the data type (array, integer, various, etc.) of the parameter. If no parameters are allowed as keywords, the word None shall be listed.

History

The history line shall consist of a minimum of two lines for each history entry. The first line shall list the data and author of the changes. The second and subsequent lines shall be the description of the changes in as much detail as necessary. The sequence of entries shall be chronologically ordered with the most recent entries at the top.

Variable Declarations

IDL's ability to easily assign data to variables without pre-declaring the variable is both a help and hindrance. Similarly, reassigning a variable with data of a different type is acceptable within IDL. Without any notification that the variable has been redeclared from one type to another, it is more difficult to determine where run-time errors may occor within programs. By using standard character naming conventions for IDL variables, the ability to track these changes is improved. To improve readability of variable names, it is suggested that the characters present below be in lower case, and the first character of the variable name following the type character be in uppercase. For example, the integer variable used as a counter may be declared as "iCount".

Integer Types

Byte

Integer data within the range of 0 to 255 (8 bits) may be stored within a Byte data type. Variables of type Byte shall begin with the letter "b".

Integer

Integer data within the range of -32,768 to 32,767 (16 bits) may be stored within an Integer data type. Variables of type Integer shall begin with the letter "i".

Unsigned Integer

Integer data within the range of 0 to 65,536 (16 bits) may be stored within an Unsigned Integer data type. Variables of type Unsigned Integer shall begin with the letters "ui".

Long

Integer data within the range of -2,147,483,648 to 2,147,483,647 (32 bits) may be stored within a Long data type. Variables of type Long shall begin with the letter "l".

Unsigned Long

Integer data within the range of 0 to 4,294,967,295 (32 bits) may be stored within an Unsigned Long data type. Variables of type Unsigned Long shall begin with the letters "ul".

Long64

Integer data within the range of -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807 (64 bits) may be stored within a Long64 data type. Variables of type Long64 shall begin with the letters "ll".

Unsigned Long64

Integer data within the range of 0 to 18,446,744,073,709,551,615 (64 bits) may be stored within an Unsigned Long64 data type. Variables of type Unsigned Long64 shall begin with the letters "ull".

Floating Point Types

Float

Floating point data within the range of +/- 10^38 (32 bits) may be stored within a Float data type. Variables of type Float shall begin with the letter "f".

Double

Floating point data within the range of +/- 10^308 (64 bits) may be stored within a Double data type. Variables of type Double shall begin with the letter "d".

Complex

Floating point data with real and imaginary parts may be stored within a Complex data type. Variables of type Complex shall begin with the letter "c".

Double Complex

Floating point data with double precision real and imaginary parts may be stored within a Double Complex data type. Variables of type Complex shall begin with the letter "dc".

Other Types

Strings

Character data stored as a string of up to 32,767 characters. Variables of type String shall begin with the letter "s".

Arrays

Arrays of data are composed of similar types of data. Variables of type Array shall begin with the letter "a", and be followed with the character of the type of data within the array. For example, an array of floating point data would begin with the characters "af".

Structures

Structures are composed of dissimilar types of data. Variables of type Structure shall begin with the letter "m", to denote the multiple data types within the structure.

Procedure Layout

The following conventions define the standard layout for each procedure in a module.

Each procedure shall be preceded by a prologue containing documentation. The prologue shall be preceded with comment delimiters. The prologue banner (described after the Procedure Layout section) shall be separated from the previous program or from the module prologue by at least one blank line that is not within comment delimiters. Shown below is a sample procedure prologue:

;---------------------------------------------------------------------------
; ListProc - Process an element of a list.
;
; This routine prints a string.
;
; Inputs: iValue, an integer value.
;
; Outputs: None.
;
; Keywords: Test, to determine if test mode is requested.
;---------------------------------------------------------------------------
   
PRO ListProc, iValue, Test=test
 
    ; create a string variable
    sValue = 'Scan ' + StrTrim(iValue, 2)
    
    ; print the list element
    Print, sValue
   
END

Function Layout

The following conventions define the standard layout for each function in a module.

Each function shall be preceded by a prologue containing documentation. The prologue shall be preceded with comment delimiters. The prologue banner (described below) shall be separated from the previous program or from the module prologue by at least one blank line that is not within comment delimiters. Shown below is a sample function prologue:

;---------------------------------------------------------------------------
; ListFunc - Process an element of a list.
;
; This routine returns a value.
;
; Reference Documents: Contract IV-1400, Paragraph 4.1.3.
;
; Inputs: iValue, an integer value.
;
; Outputs: lCalc, a calculated long integer value
;
; Keywords: Test, to determine if test mode is requested.
;
; Returns: sValue, a string
;---------------------------------------------------------------------------
   
FUNCTION ListFunc, iValue, Test=test
 
    ; create a string variable
    sValue = 'Scan ' + StrTrim(iValue, 2)
    
    ; return the string
    RETURN, sValue
END

The prologue shall consist of the blocks described below.

Banner

This shall be a semi-colon character followed by dashes across the page.

Title

This shall be one line containing the routine name followed by a short description. The routine name in the title must match the declared routine name.

Description

This shall consist of a complete description of what the routine does and how to use it. Alternatively, it shall consist of a terse description, along with a reference to design documentation which contains the complete description.

Document References

While a reference to the controlling document(s) is not required, it is recommended since it provides traceability to design and requirements. If a reference is provided, it shall consist of the keyword phrase "Reference Documents:" followed by a list of documents. See the example above.

Returns (Function only)

This shall be the word "RETURNS:" followed by a description and types of the possible result values of the function.

Program Definition

The format of the program definition statement shall be given below in order (and illustrated above):

The program title shall be first. The name shall be the same as listed in the "Title" section.

Each positional parameters shall be listed next, preferably the order specified in the "Inputs" section. If more than one line is necessary to list the positional arguments, the remaining arguments shall be indented one tab stop from the Title. If the parameters are not listed in the Inputs or Outputs sections, the parameters shall be followed by commented descriptions, one per line, for each parameter.

Local Variables

Local variables shall be listed one per line with a comment describing the use on the line preceding the variable.

Code Layout

The following conventions define the standard for the graphic layout of IDL code.

Vertical Spacing

Blank lines shall be used to make code more readable and to group logically-related sections of code together.

Blank lines shall be used to insure that standalone comment lines stand out visually and are visually associated with their logically-associated code block.

There shall not be more than one statement on a line.

The IF statement is not an exception. The executed statement shall always go on a separate line from the conditional expression. This also applies to the ELSE statement.

IF (iVal GT iCount) THEN $
    iVal = iCount $
ELSE $
    iVal = iVal + 1

Horizontal Spacing

Spaces and parentheses shall be used to make statements more readable and to group logically-related parts of statements together.

There shall be spaces around operators, after commas, and after keywords. There shall not be spaces around structure members. For example:

status = fooGet(foo, i + 3, value)
status = foo.index
status = fooArray[(max + min) / 2]
sub_string = string[0]

Lines shall fit a listing (or screen) width of up to 80 characters. Lines longer than this shall be broken into multiple lines. Most modern display devices are typically 80 characters wide, so 80 characters wide may become a de facto standard.

Continuation lines should be indented in such a way that the continued lines will not be confused with the normal code indentation. It is recommended that continuation lines line up with the part of the preceding line they continue, as in the two examples below, unless there is no obvious point of symmetry or if the obvious point results in too much indentation.

IF ((iEnvironment NE helpEnv) AND $
    (iEnvironment NE queryEnv) AND $
    (iEnvironment NE reportEnv)) THEN $
    iEnvironment = iEnvironment + 1
   
status = fooList(foo, parameterA, parameterB, parameterC, $
                      parameterD, parameterE)

Indentation

Tab sets shall be every four characters (i.e., 1, 5, 9, …). Alternatively, spaces may be used interchangeably with tabs. Most modern editors can handle the differences between tabs and spaces with equal ease in both printing and editing. Though the practice is not encouraged, most modern editors can also change groups of tabs to characters, and vice versa, if necessary.

The module and program prologues and the program definitions shall start in column 1.

One indentation shall be used after:

Style

Module Size

A general guideline is that modules should not exceed 400 lines. Files larger than this can become unwieldy for editing.

Program Size

As a goal, the average program should contain a maximum of 50 executable source statements. Software studies show that 50 statements is about what the average programmer can keep track of easily. Keeping programs to around this size will make it easier for others to understand and maintain your code. This requirement should not be construed to require an artificial splitting of cohesive code into small pieces. The deciding factors for determining program size should be cohesion and ease of understanding.

If your program significantly exceeds 50 executable source statements, you should consider breaking it down or breaking it up. If the program is very cohesive, consider breaking it down by making several higher-level programs that calls subordinates. If the program is less cohesive, consider breaking it up into several equal-level programs of greater cohesion.

Comments

Insufficiently commented code is unacceptable. Comments and references to information shall be provided throughout the code.

Optional but highly recommended: lines with braces that close blocks containing a large number of source lines or possess a high degree of nesting should contain a comment that briefly describes the block. Examples are listed below. This technique greatly aids those maintaining the software in determining the extent of each block, particularly in situations involving a high degree of nesting (multiple levels of indentation).

END ; end of function "fooGet"
ENDIF ; end if (iValue GT iSeconds)
ENDFOR ; end for-loop on each element

Case Statements

The else statement shall always be used in case statements. If nothing else, a display, print or log of the switch variable shall be executed in the else statement. The general form of the Case statement follows:
CASE (expression) OF
    0:
    1:  BEGIN
        END
    2:
    ELSE: Print, 'Unspecified expression in CASE'
ENDCASE

Type Casting

Type casting shall be labelled prior to the casting.

Precedence

Do not rely on default precedence rules. Parentheses shall always be used to show or force precedence. This is important in both arithmetical and logical expressions:
x = (a * b / 2) + c

rather than

x = a * b / 2 + c
 
IF ((a LT 2) AND (b GT 3))

rather than

IF (a LT 2 AND b GT 3)

Error Checking

The success of functions that can fail shall not be taken for granted. Return statuses and values shall always be checked for validity if they can be invalid.

In general, if any value can be invalid or out of range for a use it is destined for, it shall be checked for range or validity before that usage.

Unless other specific error-handling requirements are applicable, errors shall be handled by using the catch procedure. The folowing example is from RSI's online documentation:

PRO ABC
    ; Define variable A
    A = FLTARR(10)
   
    ; Establish error handler. When errors occur, the index of 
    ; the error is returned in the variable Error_status.
    CATCH, Error_status
   
    ; This statement begins the error handler.
    IF Error_status NE 0 THEN BEGIN	
        PRINT, 'Error index: ', Error_status
        PRINT, 'Error message:', !ERR_STRING
        ; Handle the error by extending A
        A=FLTARR(12)
    ENDIF
   
    ; Cause an error.
    A[11] = 12
   
    ; Even though an error occurs in the line above, 
    ; program execution continues to this point because 
    ; the event handler extended the definition of A so 
    ; that the statement can be re-executed.
    HELP, A
   
END

Error messages should be as unambiguous as possible and include the values of related variables to aid maintenance.

Globals

Global variables shall be kept to a minimum. Preferably, local variables should be passed as parameters instead of using global variables. If global variables must be used, it is prefereable to put them in an include file and have the programs using the variables call the include file at the appropriate location within the file. Appropriate documentation shall be included within the calling program to indicate the use of the included file.

Commons

Common variables shall be kept to a minimum. It is generally considered poor practice to rely on COMMON variables within IDL programs, and should be avoided. Particularly within widget-based code which allow multiple instances of code to be active, common variables cannot be relied on to have the correct value.

File-Scope Variables

The use of file-scope variables and associated functions are encouraged as a method of hiding data.

Structure Definitions

Structures variables shall begin with the letter "m" and the variables within a structure shall begin with the character indicative of the respective variable type (see Variable Declarations section above). An exception to this is allowed when using widget structures which require specific names and conventions.

Passing and Returning Structures

Structures shall always be passed by reference to or from functions.

Entry and Exit

Functions shall have only one entry point and one exit point. This makes software easier to test and maintain. There exist several techniques for handling errors and conditions and still providing a single exit point. One of these techniques is illustrated below.
FUNCTION SizeSlider, Vertical=iVert, Horizontal=iHoriz
    ; check keyword settings
    ; Default is Horizontal
    iVert = (N_Elements(iVert) < 1) AND Keyword_Set(iVert)
		
    ; code removed for brevity
   
    ; determine return value
    IF (iVert) THEN $
        iReturnVal = Fix(mGeo.xSize) $
    ELSE $
        iReturnVal = Fix(mGeo.YSize)
   
    ; return the value
    RETURN, iReturnVal
END

Naming Conventions

The following conventions define the standards for naming functions, variables, constants, array and structure members. The purpose of these conventions is uniformity and readability of code.

When deciding on names, keep the following in mind: the code will be written once but read many times. Make names meaningful and readable. Avoid obscure abbreviations.

Names of functions, variables and structure members shall be written with upper and lower case and no underlines. Each "word" except the first shall be capitalized. Names shall be limited to 31 characters:

iVariableName

A special case of meaningful names is the use of standard short names like c for characters, i, j, k for indices, n for counters, and x, y, z for floating-point mathematical variables. These shall be limited to local, short-term usage.

Names of defined constants shall be all upper case with underlines separating the words in the name:

F_CONSTANT_VALUE
S_TYPE_NAME

Portability Considerations

As the price:performance ratio of Macintosh and PC systems is often very favorable to UNIX systems, it will be common for customers to want to use applications on multiple platforms to maximize their investments in hardware and software. By accomodating as many of the differences in window managers and operating systems as reasonably possible, we can help them accomplish that goal. Developing code which makes minimal use of operating system commands, is highly desireable.