Alpha_1 Programmers's Manual


Alpha_1 Coding Standards

Table of Contents


C++ Coding Standards

This document describes a number of conventions which the Alpha_1 group has adopted concerning coding standards, program construction and system organization. This collection of conventions should be read by all new members of the group before they begin making software contributions to the system, and should probably be reviewed periodically by every group member. The purpose of these standards and conventions is:

  1. To achieve some degree of uniformity and coherence in the system as a whole,
  2. To make code and programs usable, accessible, and maintainable by others besides the original author, and
  3. To attempt to prevent collisions that can easily result since there are usually several group members working on the system simultaneously.

Basics

In order for the code in the system to have a high degree of uniformity and readability as a whole, it is essential that these conventions be followed. Further, a certain "style", which is hard to describe as concisely as the conventions below, has been developed in the software which has been written to date. In writing new software, and especially in modifying existing code, the first rule of our coding standards is to copy style. Although it is a subtle issue, it makes an important contribution to consistency. If you are modifying an existing module, make your code look like the rest. Even if the code doesn't follow the guidelines, yours should blend in. Of course, you also have the option of bringing the existing code up to standards, but that should generally be discussed with an Alpha_1 staff member.

It need hardly be said that programs should be written as modularly as possible. In connection with this, many utilities already exist in Alpha_1 (for example, the points library for simple coordinate geometry) in modular form which could be used easily by new programs. People working on the system should be familiar with the utilities that exist so that we don't end up with multiple versions of the same utilities. If you think there might be routines that you could use, don't hesitate to ask! Also make use of the various documentation facilities to locate existing code, and be aware that all functions which operate on a particular data structure should be declared in the corresponding header file (".h" file).

Currently, the text editor of preference for Alpha_1 work is emacs. Our version of emacs contains a C mode which allows many of the standards below to be followed painlessly. This mode is entered automatically by emacs when you read in a file with the normal C++ extension (.C).

Make all code fit onto an 79-character wide line. Even though most displays and editors now support longer lines, we must have some standard width for use by the group. This is easiest if programmers simply take care that their default editing windows on a workstation are 80 characters wide. Note that under emacs lines longer than 79 characters are wrapped, so code you write should be 79 characters wide or less.

Packaging Routines

When writing code always package related routines together in a file. However, a single file should never be very long and a single function should almost never be longer than a large emacs window (say 50 lines). For example, the symbol table file, "hash_table.c", contains routines for building and accessing symbol tables. In this file each routine is fairly small and the entire file is about 250 lines. The basic rule is that every routine should be short and comprehensible, and that recompiling a single file should not take very long. In the environment in which we work, the proliferation of files is not a big problem, due to the Unix file system and to the existence of a1make to maintain dependency information among many files.

The routines in a single file should be organized as logically as possible, with the most important routines first, and small helper functions at the end (i.e., in top-down order, not in Pascal style). An exception to this rule is inline functions which must be defined before they are used. In particular, static functions should always be defined at the end of a file.

The Alpha_1 libraries generally consist of groups of routines. Each group can be considered a package, even though there is not a formal mechanism in Alpha_1 for specifying what's in a package and what isn't. New additions to the system should either fit into existing packages or create new ones.

Naming Conventions

The proper choice of names is exceedingly important in constructing clear, understandable, and maintainable code. Named objects in C++ include variables, constants, types and structure elements, functions and macros. In addition, Alpha_1 is built of named files and directories, packages and object types, methods, programs and commands. We have developed a style of naming a large and continuously growing collection of these things which helps hold the system together and keep it flexible.

The ultimate principle behind our choice of names is to express the meaning attached to names as clearly as possible. Spend a little time thinking about that when adding a new name into the system --- especially global names that will appear in system libraries. Use tags to look for existing occurrences of names before creating a new global. Think very hard about it when adding a whole new group of names. When the meaning of a name changes, change the name to reflect it. This is easy to do consistently with global emacs commands like query-replace and grep, and with Alpha_1 tools like grepa1.

We tend to use fairly long names, consisting of up to several short words or abbreviations connected by the underscore (_) symbol, which is treated as a letter in C++. Related names often share a short prefix indicating the package to which they belong. An example is the pt_ prefix on the C++ points package functions. Don't get too extreme though. Standard line widths are 80 characters, and a few levels of indentation with a 36-character procedure name and a couple of 20-character variable names runs out of space fast! Conversely, single-character names are usually a bad idea, although occasionally it is quite clear and convenient to give the main argument to a routine a single-character name (e.g., s for a surface in a routine that accesses many of the fields of a surface).

The dynamic abbreviations (dabbrevs) package in emacs can be quite useful in keeping name spelling consistent throughout a file. In emacs, the abbreviation expansion is bound to "M-SPC". Type the first few characters of a word which appears elsewhere in the file, then execute the expansion function and emacs will find the nearest word that begins with that prefix. It if didn't get the right one, just execute the expansion again and it will look for the next one. The emacs tag functions help keep the global names within the system straight. The regular abbreviations package (abbrevs) can be used to expand commonly-used names or patterns.

Case is significant in C++ names, and is used by convention to distinguish variables and functions from constants and macros. Constants can be created with either the C++ "const" keyword, with the C++ preprocessor using "#define", or by declaring ids in an enumeration. Macros are created only with the preprocessor. Both constants (all kinds) and macros are always completely uppercased. Function names use only lowercase letters and underscores, as do names of variables, type and structure names, and structure elements. Never use mixed case in a C++ name.

Type names (from typedef, struct, class, or enum statements) always end in _type, and object names always end in _obj. Variables and functions should not have these suffixes so that searching for types can use simple regular expressions. Unfortunately there are some older functions (which predate this rule) which do use these suffixes. In time these will be removed, so don't add any new ones.

Comments

File Header Comments

Every file in the Alpha_1 system must contain a standard header. This header is generated automatically with the emacs make-c-standard-header command.

M-X make-c-standard-header
It may prompt for a user name and will always prompt for a one-line description of the file. If the environment variable USERNAME is set before emacs is invoked, this value will be used for the user's name in the header. To set up a user name, be sure the following line is in your ".login" file:
setenv USERNAME 'You R. Name'
The header contains the name of the file, your one-line comment, the author's name and address (as U of U), the date and a copyright notice. For example:
/* 
 * sample.C - This is an example file.
 * 
 * Author:      You R. Name
 *              Computer Science Dept.
 *              University of Utah
 * Date:        Wed Apr  6 1994
 * Copyright (c) 1994, University of Utah
 */

Header comments should not contain any other information. In particular, they should not contain any modification notes. By the same token, don't sign, initial, or date segments of code anywhere in a file; that information is also recorded in the checkin database. Header comments should also not contain RSC or SCCS identifier strings. Header comments should contain only the information shown in the example above.

Major Comments

Comments always appear at the same indentation level as the surrounding code (unless, of course, the comment is on the end of a line of code).

Every comment which is on a separate line from the code and takes more than one line must be formatted in major comment form:

/* This is the comment which takes more 
 * than one line and must be put in
 * major comment form.
 */

Note the alignment and the fact that the */ must appear on a line by itself. Also the comment begins on the same line as the open string "/*", not on the next line. Don't get fancy making boxes or other decorated comments. The two forms shown here are what we want. A major comment can be formatted automatically in emacs with "M-:" which enters c-comment mode. To exit comment mode type "M-C-c".

Single line comments must use the /* */ form; the // form which is available in C++ is not to be used in Alpha_1.

Comments should almost always be in correct English, especially "global" comments. This implies full sentences for descriptions more than one line long. Every comment must begin with a capital letter and end with a period even if it is a not a complete sentence.

Tags

Everything should be tagged with the Alpha_1 tag string. A tag is simply a C++ comment that looks like this:

/****************************************************************
 * TAG( name1 name2 )
 */
Note that more than one word may be placed in a "TAG(...)" clause, but that the clause cannot span more than one line. For instance, this is wrong:
 * TAG( trace_flags a_buffer adj_flag alpha_output
 * color_flag cornell cosine cull double_res )
This is right:
 * TAG( trace_flags a_buffer adj_flag alpha_output )
 * TAG( color_flag cornell cosine cull double_res )

In particular, you should tag the following items:

Function Header Comments

Every function definition must be preceeded by a function header comment. (Note that the "extern" declaration for a function should be tagged, the but body of the comment should be empty.) There is a basic style, and an extended style (which is a newer, optional, coding standard). Large parts of the Programmer's Manual are automatically derived from the newer extended style comments in the source files. In order for this to work, the header comments for functions must exactly follow the conventions described here.

The basic style contains a TAG entry and a short description as follows:

/****************************************************************
 * TAG( anchor_obj::copy_obj )
 * 
 * Virtual Copy function for "anchor_obj" object.
 */
object_type *
anchor_obj::copy_obj() const
{

}
The extended style is meant for all externally accessible library functions, but it is left to programmer discretion. Besides documenting the code, these headers are used to automatically generate system documention.

The extended style has a minimum configuration plus a number of optional fields to separate various internal and external documention. Here is an example of the minimal extended style function header:

/*****************************************************************
 * TAG( pt_obj::distance )
 * 
 * Determine the distance between two points.
 * 
 * Arguments:
 * 	p:	Other point.
 * 
 * Returns:
 * 	Distance between <this> and <p>.
 */
real_type 
pt_obj::distance( const pt_obj &p ) const
{

}
The TAG, short description, Arguments, and Returns fields must always exist. If there are no arguments or no return, "None." should be entered.

Here is the full form of the extended function header with all the options:

/*****************************************************************
 * TAG( do_it )
 *
 * One-line or short paragraph description.
 *
 * Following a short description of the function may be further
 * paragraphs about what this function does. This information should
 * be aimed at giving people information about why and how they should
 * call this routine, and what they can expect it to do.  The
 * descriptive information which appears in this section will appear
 * in the printed manual.
 *
 * Arguments:
 *      name:		Argument description.
 *      flag:		(Default: FALSE) If TRUE, do something else.	
 *
 * Returns:
 *      Return value description or options.
 *
 * Side Effects:
 *      Argument modifications etc.
 *
 * Assumptions:
 *	The caller knows what they are doing.
 *
 * NOTE:
 *      You need to be very careful when using this function.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * Information about the internal algorithms and how the routine
 * actually works may be added either in comments internal to 
 * the routine or in this section.  Note that a "space-star" row
 * is used to indicate that this is internal information while the
 * descriptions above the line describe the external
 * interface.  The information in this paragraph will not appear
 * in the printed manual.
 *
 */
foo_obj *
do_it( string_type name, boolean_type flag )
{

}
The short description at the beginning is optionally followed by a paragraph or several paragraphs with more detailed description. The paragraphs before the predefined fields should describe the external interface (what you have to know in order to call the routine), while paragraphs at the end of the function header comment, if present, contain information which is internal to the routine.

All fields except Arguments and Returns are optional and can be removed if empty. Optional fields, if they exist, must appear in the indicated order.

The Arguments field should list all arguments in order with a short description for each. The only exceptions are helper functions (see below). Indent all the descriptive text using tabs so that it is all at the same indentation level. It may start on the same line or on the next line, but must be consistent through all the arguments. It is ok to group several arguments together as long as they are logically related, all have the same type, and occur sequentially in the procedure call. For example:

      pt1, pt2:	The two endpoints of the line segment.
Default values for optional arguments should be specified as part of the argument description.

The Returns field describes special properties about the returned object (e.g., structure, attributes). If the return type is "void", enter "None." here.

The optional Side Effects field should list all modifications to all objects except the return value. This includes modification of arguments and/or global data.

The optional Assumptions field may list the assumptions made in the procedure. Putting into words these (sometimes implicit) assumptions is difficult, but can make life easier for others.

The optional NOTE field is meant as an eye catcher. If there is something that doesn't readily fit into the other fields, that you want to make sure that readers pay attention to, use this header.

It may sometimes be convenient to have helper functions that are invoked from an interface function. These functions share most of the arguments and are usually in the same file. In such case one can substitute "See function ..." in the appropriate fields.

In all of the above fields, there are conventions for refering to argument and function names. Identify argument names with angle brackets (e.g., "Specify a value of TRUE for <shell_flag> if you want this action."). For functions, use null parentheses (e.g., "This is very similar to the foo_bar() function"). Since these comments can be used to directly generate documentation, these conventions allow the recongnition of function names and formal parameter names for special formatting.

Sometimes (especially for virtual methods with fixed calling sequences), an argument is unused, and its name does not even appear in the function declaration, just its type class. In these cases, the argument need not be listed in the "Arguments" section of the comment.

Emacs Template

There is an interactive emacs function to make extended function headers. In a .C file, execute "M-x make-function." Emacs will prompt you for a function declaration. Type in the full function prototype in C++/Ansi C notation. For example:
srf_obj *surface_of_rev( crv_obj *axes, crv_obj *profile )

This will be expanded to:

/*****************************************************************
 * TAG( srf_of_revolution )
 * 
 * 
 * 
 * Arguments:
 * 	axis:		...
 * 	cross_section:	...
 * 
 * Returns:
 * 	...
 * 
 * Side Effects:
 * 	...
 * 
 * Assumptions:
 * 	...
 * 
 * NOTE:
 * 	...
 * 
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * 
 * ... 
 *
 */
static shell_obj * 
srf_of_revolution( vector_type *axis, crv_obj *cross_section )
{
    
}

The cursor will be positioned at the proper line, ready for you to fill in the description. Fill in the remaining ellipsis (...) as needed. You can use "C-c ." (ctl-c dot) to get to the next ellipsis, erase the dots, and position your cursor correctly. Once you are done filling in the template, exit the recursive edit mode you are in by using "M-C-c". The unset ellipsis will be replaced by "None." and unset optional fields will be removed. The cursor will be placed in the function body. If there is no internal documentation for the routine (i.e., just the ellipsis remains after the star-space row), then this section will also be removed.

Include Files

Most programs will need to include certain header files which define constants and data structures shared by many programs. These header files are in some sense the key to the way we use C++ in Alpha_1. The Alpha_1 code is really a specialized language of its own, which is defined by the operations available through header files. (As an example, list manipulation is in every sense a primitive in Alpha_1 C++ code, though it is not in the C++ language.)

There is a standard for framing all header files with a #ifndef...#endif pair to prevent the definitions from being loaded twice in any source file.

/* Normal file header comments. */
#ifndef TRIMH
#define TRIMH

/* Contents of include file. */

#endif /* TRIMH */
Notice that the closing "#endif" is includes a comment containing the controling symbol. ANSI requires that this comment must be enclosed in a C++ style comment. All header files must begin with a header comment, followed by the #ifndef framing, followed by any required includes.

In some cases, a header file requires other header files to be included before it will compile correctly. Appropriate #include lines should appear in header files that would fail to compile otherwise (but do not include "alpha_1.h" in any header file). This avoids the problem of properly ordering header files in C++ code. The programmer never has to remember which header files are prerequisites of others. Note that the notion of "requires to compile" is not exactly obvious. In the following example the first extern declaration does not require an extra header file, but the second declaration does.

/*****************************************************************
 * TAG( uniform_knot_vector )
 */
extern knot_vector_obj *
uniform_knot_vector( int npts, boolean_type ec_flag, int k );

/*****************************************************************
 * TAG( crv_uniform_knot_vector )
 */
extern knot_vector_obj *
crv_uniform_knot_vector( ctl_polygon_obj *ctl_pts, ec_type ec_flag, int k );
This is because all objects like "ctl_polygon_obj" are forward declared in "alpha_1.h", but enumerations like "ec_type" are not. The "acid test" for whether an include is necessary to compile is to write the C++ file:
#include <alpha_1.h>
#include <your_test_header.h>
This two line file should compile without errors and "your_test_header.h" should include as few other headers a possible.

The header files should never contain function or variable definitions except for inline functions. Also, all variables and functions should be declared as extern. All subroutines and variables which are known in more than one file must be declared as extern in an appropriate header or str file. Variables (not local to a function) and functions which are known in only one file must be declared static at the top of that file.

Functions declared in header files should have tags but no comment text. (The static function declarations at the start of the source code should not have comments or tags.)

Source Files

Like other system files, source files must begin with a file header comment. Immediately following the header comment are the include statements. Non-Alpha_1 header files are listed first (e.g., "string.h"), followed by "alpha_1.h" which is required for all Alpha_1 source files, and then other Alpha_1 include files. Only include header files that are absolutely necessary.

Immediately after the #include lines are any global variable definitions for the file. Following global variable definitions are any preprocessor macros. It is rarely necessary to use these in C++ and each use should be carefully considered. Static (local) function declarations (not definitions) appear next. These forward declarations should not have TAG comments. Next come static variable definitions (which should be tagged). The remainder of the file should just be function definitions. The bodies of static functions should always be at the end of a file.

Defined Constants

The C++ "const" feature should be used wherever possible for parameters such as array sizes. Think carefully before allowing a hard-wired number to appear in your code, especially if the same number appears more than once for the same purpose. Also, avoid the preprocessor whenever possible. C++ const and inline features offer better alternatives in almost all cases.

The constant entry itself should appear only once. If more than one file uses the parameter, then the constant must occur in a header file (".h") which can be shared by the different files.

Do not use constants as a replacement for enumerated types. C++ handles enumerated types properly so use them. Also use C++ inline functions instead of preprocessor macros. These functions are type checked and generally less error-prone than macros. Think hard before defining a macro which cannot be expressed as an inline function.

Never use the preprocessor to "cover up" syntax you don't like. For instance,

#define DECLARE_ARGS 	Arg args[MAX_ARGS]; int n = 0
#define GIFT( a )	get_int_from_text( a )
These are both deemed improper use of the preprocessor. The first attempts to hide the problem of declaring two local variables repeatedly in a collection of functions. This is not a "problem" to be hidden, just write the code. The second attempts to overcome the problem of a function name which is inconveniently long. Here the obvious fix is to shorten the function name.

Function Declarations

All functions must be declared as

External function declations are tagged, statics are not.

Function declarations always begin in the left-most column. Functions must always declare a return type, and it appears on a separate line before the function name. If the function is static, the static keyword appears on the same line with the return type. If all the parameter declarations will fit on one line, then they should appear that way. Otherwise, they must be placed one per line, indented underneath the first one. There are spaces after each comma, and before the first argument and after the last argument. There is no space between the function name and the open parenthesis. There are no blank lines between the closing */ of the function comment and the open brace which begins the body of the function.

static knot_vector_obj *
srf_uniform_knot_vector( ctl_mesh_obj *ctl_pts,
			 array_dir_type dir,
			 ec_type ec_flag,
			 int k )
{
    /* Indented code begins here. */
}

static knot_vector_obj *
crv_chord_knot_vector( ctl_polygon_obj *ctl_pts, ec_type ec_flag, int order );
{
    /* Indented code begins here. */
}

Constructors for classes have a particular format. They look like:

mi_type::mi_type( size_t s,
		  int socket,
		  int err ) :
    memory_block_type( sizeof( mi_type ) ),
    id_hash_table( HASH_TABLE_SIZE )
    server_socket( socket ),
    err_socket( err ),
    last_error( -11 );
{
    /* Other initialization code... */
}
The argument list is treated normally (if it doesn't fit on one line declare one variable per line). Next comes the initialization list which is used to initialize base classes and data members. If this initialization list fits on the same line as the function name and arguments place it all on one line, otherwise initialize one base class or data member per line indenting by four spaces for each item.

Local variables should be declared in the nearest enclosing block of code. Variable declarations may occur anywhere in the block. Be very careful with local variables in switch statements. The following example shows a problem with naive use of local variables in switch statements:

switch ( tag )
{
case T_CRV_OBJ:
    point_type pt(1,2,3);  
    ...
    break;
case T_SRF_OBJ:
    use_pt( pt );   /* Illegal C++ */
    ...
}
If the T_SRF_OBJ case is taken the local variable pt will exist, but will not have been initialized to "(1,2,3)". This is clearly defined in the language, but bears repeating here. Similarly, the compiler allows the declaration to be placed after the open brace, but the code is never executed so no initialization (like object constructors) will be performed. Enclosing the body of the case in braces to define a new block is reasonable, but the break statement must go after the closing brace, otherwise the break will cause the program to skip executing the destructor. Basically, this is very complicated, and it is recommended that declarations within a switch be avoided in general.

Within a group of local declarations, the following ordering is suggested:

Declarations with initializations are considered assignment statements and are limited to one per line. If at all possible, initialize variables at declaration time. A blank line should follow a group of declarations.

Gobal Variables

Global variables should be avoided at all times. If you find it necessary to declare global variables for a program or package you should declare a class which encapsulates your global values. You can then declare a single instance of this class for use in your program. Global declarations always begin in the left-most column. It is usually the case that global declarations belong in separate files which are linked in separately, and can thus be accessed by various programs which use common subroutines.

When you declare a global variable you should also encapsulate the access to the global class' slots. Here is an example from the render program:

/*****************************************************************
 * TAG( render_state_type render )
 * 
 * This class contains the global variables controlling the render
 * program. 
 */
class render_state_type
{
    int			r_o_pixwidth,
    			r_o_pixheight;

    boolean_type	r_a_buffer,
    			r_alpha_output,
    			r_clr_interp,
			...
    			r_z_output;


public:
    render_state_type();
    ~render_state_type();

    boolean_type	a_buffer( boolean_type a = BADINT );
    boolean_type	alpha_output( boolean_type a = BADINT );
    boolean_type	clr_interp( boolean_type c = BADINT );
    ...
    int			o_pixwidth( int n = BADINT );
    int			o_pixheight( int n = BADINT );
    ...
    boolean_type	z_output( boolean_type z = BADINT );
};
For globals used by more than one program you should use the naming convention, package_state_type, where package is the name associated with the collection of variables. Note that the accessor member functions for these values are used both to set and get the values. Also note that the accessors are not inline functions. This allows programmers to override the accessor function easily and to breakpoint in the accessor.

Nesting and Spacing

The indentation nesting is 4 spaces per nesting level. No code should start in the left-most column. Braces should appear on a line by themselves, in line with the enclosing level, and the statements enclosed by the braces should be indented. In the do-while and repeat-until constructs, the while or until may appear on the same line with the closing brace.

An else should be aligned with the corresponding if; this includes the else if construct:

if ( foo )
    ...
else if ( bar )
    ...
else ( something )
    ...

The body of case statements are indented for switch statements, as in:

switch ( ob->tag() )
{
case LABEL1:
    i = j;
    ...
    break;

case LABEL2:
case LABEL3:
    i = j+1;
    ...
    break;

default:
    break;
}
If all the case statements are a single very short line, the following short form is allowed, but the code must line up as shown:
switch ( ob->tag() )
{
case LABEL:       i = j;      break;
case LABEL_LONG:  i = j+2;    break;
case LABEL3:      i = j+size; break;
default:                      break;
}
With the exception of the special form of the switch just mentioned, there is a strict rule that only one statement may appear on a line. While the sequential assignment form a = b = c; is allowed, using the comma operator to make a single statement out of several is not allowed. The comma operator may only be used in for loop initialization.

For all looping constructs and conditional statements (including if and else) the action or body must appear on a separate line, even if it is a null statement.

for ( i=0; i<3; i++ )
    p.abcd[i] = 0.0;

while ( ptr1++ != ptr2 )
    ;

if ( flag )
    flag = FALSE;
else
    flag = TRUE;
Watch for boolean expressions that can be simplified. The conditional above is better written as:
flag = !flag;
A ?: operator with TRUE and FALSE on either side of the colon, should always be rewritten without the conditional. And
return a < b;
is preferred over
if ( a < b )
    return TRUE;
else
    return FALSE;
Subroutine calls have the same spacing conventions as subroutine declarations: no space between the name and the open parenthesis, spaces around all the arguments. If you need just one or two spaces to fit a function call on a single line, you may leave out some of these spaces. Operators in the language (if, else, while, for, etc.) should not look like function calls; insert a space before the open parenthesis. Spaces are still inserted inside the outer level of parentheses (at least) for these operators. Don't insert extra parentheses for operators that don't need them. Some examples of these conventions are:
foo( a, b, c );    /* Function call. */

while ( a )
    ;

if ( b > a )
    b = 1;

for ( i=0; i<3; i++ )
    a[i] = b[i];

new foo_bar_obj;

delete poly;

return a;

Always put spaces around an assignment operator.

In for loops, the initialization, conditional, and incrementing expressions may appear on a single line if they all fit. If not, separate the expressions at the semicolons (at least), indenting under the first initialization statement. Always put spaces after the semicolons in the single-line form. The comma operator may be used to specify several initializations or incrementing expressions, but the multi-line form should generally be used in this case. Be careful that the body of the loop is clearly distinguished from the control logic if that logic is very complex. Braces might be helpful for a single-line action, even though they're not strictly necessary. For example:

for ( ptr1 = a->n(), ptr2 = b->p();
      ptr1 != ptr2;
      ptr1 = ptr1->n(),
          ptr2 = ptr2->p() )
{
    fn( ptr1, ptr2 );
}

Comments and white space should both be used generously but judiciously. It is difficult (but not impossible) to use too much of either. The C-mode commands for comments are very helpful; documentation can be found in the emacs section of the info-tree. Two consecutive blank lines are never needed within a function, although occasionally the extra space is nice between functions. More than two consecutive blank lines are never used. A blank line is always nice after a close brace if the next line begins with a comment.

In general, assignment statements are easier to read if the right hand side of the assignment is to the right of the equals sign. For simple code this is obvious

i = a * b + c;
But for more complex assignments (often including long function names) this becomes more difficult. A simple rule is that code should appear to the right of the equals sign whenever possible. Function arguments and arithmetic expressions should be aligned vertically on the left.
a_very_long_name = a + very + long +
                   expression;
another_very_long_name = with_a_long_function_name(
                                with_long_argument1,
                                with_long_argument2 );

Other Language Features

In C++, it is quite easy to build up long chains of method calls and slot accesses. This is encouraged, up to what will fit on a single line. It is confusing to break lines at a -> or . structure reference, so temporary variables should be used for clarity in cases where the sequence will not fit on a single line.

Most explicit casts are unnecessary in C++. Do not put them in until the compiler insists on it, and think carefully about whether a cast is really the "right" answer when you do add them. Casting anonymous or base pointers (like attr_obj) to a derived type is the most common (legitimate) reason for an explicit cast. You never need to cast an object to object_type. If the compiler complains that such a cast is necessary you have forgotten to include the appropriate header file.

Do not declare variables to be register unless you know what you are doing and really understand the mechanisms which compilers use for allocating registers. The compiler probably makes better choices than most of us will. In fact, on the HP workstations the compiler will ignore any register keywords!

Never call malloc or free; use the new and delete operators. Allocate from the stack when appropriate; there is often no need for dynamic memory for structures that are allocated and freed in the same routine.

Occasionally in C++, a line of code must be added to avoid compiler warning messages, even though it is not necessary for correct code (usually initializing a variable). In these cases, the exact comment shown below must appear on the line:

x = y = z = 0.0;    /* Keep C++ quiet. */
One common situation which seems to require such a bogus assignment but has a better solution is the following:
void
foo( int unused1, int )
{
    unused1 = 0;     /* Keep C++ quiet. */
}
If an argument is unused the compiler generate a warning. You might try avoiding the compilation warning by inserting a bogus assignment. This is the wrong fix. Instead simply leave out the variable name in the function declaration (like the second argument above).

There are several C++ language features which are to be used with extreme caution or not at all.

Summary of Coding Standards

The Alpha_1 Coding Standards Summary contains a summary of the most important standards. Here are some additional items, suggestions, and exceptions.

Index

c

  • case conventions in C++
  • constants
  • copy style rule

    d

  • defined constants
  • dynamic abbreviation

    g

  • global variable conventions

    h

  • header comments in C++

    n

  • naming conventions in C++
  • nesting conventions in C++

    p

  • packaging routines

    s

  • spacing conventions in C++
  • standard headers in C++

    t

  • tags
  • type naming conventions

  • Alpha_1 Programmer's Manual Home Page
    Alpha_1 Programmer's Manual. Version 95.06.
    Copyright © 1995, University of Utah
    alpha1@gr.utah.edu