ABLE Development Conventions
Version 0.4


Table of Contents


1 Introduction

Setting a consistent set of conventions for software development is necessary to insure smooth integration, evolution, and maintenance of the software being built. The ABLE group has specified the following set of conventions for development of the Aesop system and its associated tools.

2 Coding Conventions

2.1 Naming Conventions in C code

  1. All C functions in a module MOD start with MOD_. Example: fcl_init.
  2. All C functions that are Tcl commands start with a module prefix and end with Cmd. Example: fcl_initCmd.
  3. Multiword names are spelled like this: oneTwoThree
  4. Non-pointer type names start with T, like TFooStruct pointer types start with P, like Pfoo. For example, an fcl type that isn't a pointer would be fcl_TFooBar an fcl type that is a pointer would be fcl_PFoo
  5. All globals, static or otherwise, start with g, as in fcl_gGlobalVar.
  6. All C code should be formatted in the same style obtained by running the following command over the file on rojen or rhodope: indent -br <file>.c. For emacs users, this is easy, just use C mode. For others, run indent before checking files in to CVS.
  7. #include guards should be put in all .h files to avoid multiple inclusions.

2.2 Tcl Conventions.

  1. Procedures that are internal to a module MOD start with __MOD__. For example: __fcl__init.
  2. Procedures that are exported from a module MOD start with the same prefix as in C code. Example: fcl_aProcedureName.
Note: This isn't actually in use yet. Currently, the __MOD__ prefix is generally used. We should maybe change this.

  1. When writing utility procedures that are not specific to one module, use the prefix associated with the repository where the procedure will either eventually live, or would live if it were put into the repository. See section 4.2 for a list of the available repositories and their associated prefixes.
  2. Multiword names are spelled like oneTwoThree.
  3. Code is indented according to tcl mode in Emacs. One indent level is *4* spaces.
  4. Pete tries to put spaces between braces and their contents, like: [ expr $a+$b ]. But this is mostly to make Emacs color the word expr correctly.
  5. We need to develop a convention for naming type encodings in tcl and fcl. This convention should indicate that the item is being used to create, regulate, or manipulate a higher-level data structure. This convention has not yet been completely specified.
  6. These naming conventions may be over-ridden when creating a procedure that should appear to the caller to be an extension to the tcl or fcl language. Examples include the procedures "class," "method," and "slot."
  7. When initialization code is necessary in a file that should be "sourced" at startup, the initialization routines should be placed in a procedure that includes "init" somewhere in its name. The procedure name should clearly indicate that it is the procedure that should be called to initialize a given module. If it makes sense to have this initialization routine automatically called the first time that the file is sourced in, then place a call to the initialization procedure at the end of the file, and surround the procedure call with an if statement that insures the procedure will be called only the first time that the file is sourced.

2.3 Fcl Conventions

  1. FCL class names all begin with C, and are then named like mutiWordNames. For example:
    CfamConnector.
  2. Names for data slots start with a small `s', for `slot.', as in `slot CfamObject sName'
  3. Method names are named like C functions.
  4. Private methods that are for internal or low level use only should have a name that starts with two underscores: e.g. "__setParent".
  5. Eventually, we should get rid of all uses of get/set slot except in private code.

2.4 General Coding Conventions

  1. External interfaces: The interface between a tool and the outside world should be as general and well defined as possible. In general, where an external tool might later be hooked in to replace some of the functionality that is currently being done locally, the location where changes will need to be made should be as localized as possible.

3 Change Control Procedures

The procedures that the project uses for source code control are outlined in the document Source Code Control Procedures for ABLE. This section deals with the procedures for submitting requests to change the current code base in order to fix major bugs, or bring up design and process issues with the rest of the group.

We will be using a two-layered system. The first layer is informal and meant is generally used when you are working on a piece of code that is in active and rapid development. For small changes to such pieces of code, sending a zephyr-gram or e-mail to the person responsible for the code is a reasonable action. For example, if you are working on new extensions to the database class library, and you find an obvious piece of brain-damage in some code that Pete wrote earlier, please tell him quickly in a zephyr-gram. Pete is a real zephyr-head.

For bigger fixes, and especially design and process issues, we have set up some scripts to keep a database of change requests. In order to access this database, you can use the scripts directly (not recommended for the squeamish) or you can use the crtool command from your shell. This program is located in /usr/local/bin so you should have that on your path. It runs several other scripts that are located in various places, but it is preconfigured to find them so you don't have to worry about any special setup.

This tool will bring up a window that looks like this:

The "Search For" entry allows you to specify a regular expression to search for in the database. By default, if any field of a record matches the regular expression then a summary line will be shown in the list below when you click on the List button. When you click on the Find button, you will get a long listing of any records that match the search string. Long entries look like this:

Double clicking on any entry in the summary list also gets you a long listing of just that record. The Show button is an alias to double-clicking on a record in the summary list view. The Field button is a menu button that allows you to search for text matches in only specific fields of a record.

Over to the right, the Assign, Close, and Defer buttons operate on the currently selected record. A record is selected if you clicked on it in the summary list view or if it is the only record shown in the long list view. The User button pops a menu that lets you pick who you want to annoy by assigning her work. Only wizards can use these buttons, they should be grayed out for mere mortals.

The Print button prints whatever is in the list box to the printer named in your PRINTER environment variable. It will be more intelligent Real Soon Now.

The Submit button brings up the following form for you to fill out:

You just use the menu buttons to choose the right values and type the appropriate text into the text boxes. Here's what stuff means:

The change type can be either a Bug, a New Feature, or something else.

The change class can have the following values and meanings:

Design The change is meant to be to the basic design of some part of the system
Implementation The change is meant to be to the implementation of some part of the system, but it is not visible at the user level.
User Level The change will be user visible.
Process You don't like how something in the group works. E.g. you think Pete is a putz and should never write another line of code.
The module name refers to the CVS module in which the change would occur.

Finally, the severity is how important you think this change is.

The form won't let you submit without entering something for the Module and the Summary. You can cancel at any time. Submitting a request puts the new record in the database and sends mail to the Change Control Board.

Every Monday, the Change Control Board (Pete, Rob, and Bob) will meet and assign people work out of the current pool of CRs. You will receive mail from the board when you are assigned work.

4 Commenting Conventions

All source code files should begin with a header in the following format:

# -*- tcl -*-                
# ----------------------------------------------------------
# File: $Id$
# Author: <author>
# Project ABLE 
# 
# Abstract: The purpose of the file or the routines stored
#               in the file.
# Uses: list of external facilities this file depends on.
# Contents: <a list of the procedures defined in the file>
# Notes: any misc. information that people should know
# Revision History: <date> <person> <_short_ summary>
# -----------------------------------------------------------
# rcs information
set __dummy ""
set __MOD__version "\$Id $__dummy"
The following format appears to have become a de-facto standard for commenting procedure headers. Please use it in your code:

# ---------------------------------------------------------
# procName [optional arg list]
#
# summary: describe what the procedure does here
#
# args:   arg1 - type of arg1 parameter, purpose, constraints
#            arg2 - type of arg2 parameter, purpose, constraints
#
# preconditions: list of preconditions that must be met
#              to assure that this code will run properly.
#
# returns: what the procedure returns, for both success and
#               and error conditions. If this is a procedure that 
#              should not have a defined return value then enter
#              "undefined" here.
#
# exceptions: errors or exceptions that can be thrown
#
# side effects:
#
# notes:
# ----------------------------------------------------------
In addition to comments at the beginning of the procedure, long procedures should generally have comments throughout them. Although profuse commenting is encouraged, it is more important that the comments be of high quality than high quantity. Non-obvious design and implementation decisions need to be commented, as does the format of data structures, assumptions that the code makes, dependencies that a procedure has on other procedures, and design rationale. Global variable declarations should be localized and flagged. In general, you do not need to comment things that would be blatantly obvious to a person looking at the code five years from now (i.e. i = i+1; /* increment i */). The trick here, of course, is figuring out what is obvious and what is subtle. When you are unsure whether something is blatantly obvious, err on the side of adding too many comments.

Classes and methods should also have standard header comments. Use the following for classes:

# ---------------------------------------------------------
# class CFamObject
#
# summary: Summary of this class' reason for being
#
# slots: <list of slots in each instance>
#
# methods: <list of available methods>
#
# notes: <anything else>
# ----------------------------------------------------------
Methods should have the following header, which is pretty much identical to the one for procedures.

# ---------------------------------------------------------
# method methName class [optional arg list]
#
# summary: describe what the procedure does here
#
# args:   arg1 - type of arg1 parameter, purpose, constraints
#          arg2 - type of arg2 parameter, purpose, constraints
#
# preconditions: list of preconditions that must be met
#              to assure that this code will run properly.
#
# returns: what the procedure returns, for both success and
#               and error conditions. If this is a procedure that 
#              should not have a defined return value then enter
#              "undefined" here.
#
# exceptions: errors or exceptions that can be thrown
#
# side effects:
#
# notes:
# ----------------------------------------------------------
Finally, for all you emacs weenies out there, the following chant will provide you with a nice menu of the functions defined in a particular code file. This works in lemacs and emacs-19, but not in other versions, add it to your ~/.emacs file.

(setq load-path
   (append
   (list "/usr/local//lib/emacs/")
   load-path))
(require 'func-menu)
(add-hook 'find-file-hooks 'fume-add-menubar-entry)
(define-key global-map "\C-\M-g" 'fume-prompt-function-goto)
(define-key global-map '(shift button3) 'mouse-function-menu)

5 Conventions for repository procedures.

Writing general procedures that can be (re)used in multiple places of the system is highly encouraged. In order to facilitate this internal reuse we have created a repository system that includes a rudimentary database and graphical browser, a set of naming conventions for procedures stored in the repository, a set of code, interface, and parameter conventions, and processes for submitting, reviewing, and retrieving items stored in the repository. A brief description of the coding conventions appears below. For a more detailed description of the tools available to support the repository and the processes we have adopted for using it, please see the ABLE repository documentation.

5.1 Repository Structure

In order to add some structure and retrieval guidance, the repository is currently divided into four sub-repositories - Tcl general, Fcl related, Tk general, Tk widgets. As the names suggest, each of these sub-repositories stores a set of procedures that are related by common functionality and the type of entities that they manipulate. The initial repositories are going to store tcl/fcl code only. I will set up repositories for C/C++ or other types of code when and if we feel it is necessary. An overview of the types of things that should be put in each repository follows:

Tcl General (tclGeneral) - This repository holds routines that provide common functionality in raw tcl. Examples include: routines to manipulate lists, strings, files, and other data structures, global variable management routines, and short routines that do common tasks but are tricky to get "just right."
Fcl Related (fclRelated) - The fclRelated repository holds routines that are designed to manipulate fcl objects or interact with the persistent object store. These routines may be written either in raw tcl, or fcl.
Tk General (tkGeneral) - tkGeneral stores routines that manipulate or manage tk widgets. Routines in this repository should not actually create a widget themselves. Examples of appropriate procedures to put in this repository include: loading a text file into a text widget, counting the number of items on a canvas, filling a list-box with a list, bindings to make a text or canvas widget work conveniently (and consistently)
Tk Widgets (tkWidget) - tkWidget stores routines that create new widgets and meta-widgets. Examples of appropriate items to store in this repository would be: mkDialogBox, mkScrollingCanvas, mkSelectionBox, etc.

5.2 Repository Procedure Naming Conventions:

Procedures stored in the code repository should follow a naming convention that is similar to the conventions used elsewhere. It is particularly important that procedures stored in the repository have names that clearly articulate what they do because procedure names are what people will generally search through when trying to find a component to reuse.

The general convention is that each sub-repository will have a special prefix that should be prepended to a procedure name when it is put in the repository. Most of the other ABLE conventions should be in effect in addition to this prefix, especially the oneTwoThree format for multiword names. The prefix for each of the sub-repositories and example procedure names for each of them follows:

Tcl General: prefix = util_ , example = util_findInitSymbolInString, util_initBrowserGlobals
Fcl Related: prefix = futil_ , example = futil_openDatabaseByName, futil_scanShelfNames
Tk General: prefix = tkutil_ , example = tkutil_loadTextFile, tkutil_saveListBox
Tk Widget: prefix = mk, example = mkScrollingCanvas, mkDialogBox

5.3 Code structure, interfaces, and error handling:

It is important that we use fairly standard call and return signatures for the reusable routines. The following list contains conventions and rules-of-thumb that you should follow when making reusable code fragments. Please consider this an incomplete list and add to it as it makes sense.

  1. When making complex procedures, such as one to make a dialog box or toplevel window, hide as much of the core functionality as possible in a central, hidden procedure. Add various wrapper functions that call this centralized procedure in different ways, and only publish the wrapper functions in the repository. The core procedure set will need to be stored in the repository also, but it should be relatively invisible. If this is done correctly, only the wrapper functions will be visible in the browser.
  2. When a window is required as a parameter, require that the name passed in be a legal window name that exists. Do not prepend the name passed in with a period. Assume that the leading period is given in the window name.
  3. Error Handling. We need to specify a good set of conventions for dealing with errors, checking for valid input parameters, and throwing exceptions. Currently we don't have a good set of conventions here.

6 Error and exception handling:

For now, we will define three classes of errors that can happen in our system:

  1. Errors generated by calls into the persistence library, or related to the operation of the Exodus Storage manager client.
  2. Errors generated by calls into FCL class library, such as when database constraints are violated.
  3. Errors generated by calls into either the Tcl or Tk command sets.
The FCL runtime library has control over the first two of these classes, so we would like to distinguish them from the third and try to provide somewhat more meaningful messages when they happen. To do this, we will take advantage of the Tcl return mechanism and define return codes that are specific to our runtime system.

Real Soon Now, the FCL runtime system will define the following new commands related to error handling:

famError <msg>
Throw a FCL runtime error with the message msg.
famCatch <command> <error-command>
Catch errors in command and do special processing for FCL runtime errors using the code in error-command Returns as normal
abort <msg>
Abort a transaction with the error message msg. This is a redefinition of the original abort command.
Note that famCatch will only recognize as special errors thrown by famError or abort. This facility is still under construction, so more information will be available after the design is complete.

7 Typograhic Standards

All documents for the ABLE project are built with Framemaker and related tools using the same style template. The template is oriented around a tech-report like layout, as opposed to books or theses. This section spells out the typographic conventions that are not made explicit in the template:

  1. For code that is in its own paragraph, start with the 1Code style (or 1NumCode for code with numbered lines) and it will continue in the Code style. Code is defined as any C, Tcl, or Framemaker construct containing keywords that should be separate from running text.
  2. For in-line code, use the Code character style.
  3. For descriptions of code, use the CodeDesc style.
  4. File names in code should be formatted using the Constant style, like "go open up foo.c and look at the code."
  5. Use Body paragraphs for the first paragraph in each section, and to start the body text up again after a code block, a figure, or a table. This provides separation between body text and the object that interrupted it.
  6. Use the Emphasis style when introducing a term that you doing to define (i.e. An object is an encapsulation of some local state with a collection of procedures called methods that manipulate that state).
  7. For longer descriptions, use the Description style.
  8. Cross references to external documents should be tagged with the Emphasis character style, like "Go read the FCL Users Manual for more info."
  9. Finally, keystrokes and other user gestures should be formatted the same way they do it in the GNU documentation. For control keys, you say, for example, "Control-c" or "C-c". For meta keys, you say "Meta-c" or "M-c", and for mouse buttons you say "Button1". Control-button-1 would then be "C-Button1", meta and button 1 would be "M-Button1", or "M-B1". All of these should be in the character style Code.
 

Bob Monroe and Peter Su