CmpE130 Lab Documentation
Design Report Format
The design report should be structured in numbered sections 1-7. Each section should be divided into numbered subsections 1.1, 1.2, etc. as required. The structure for doing this is not specified. However, the contents required in each section are specified. You may find that adding other sections may make your discussion clearer.
- Title Page
The title page is not a numbered section. Please remember to put your name on the title page. - Table of Contents
The table of contents is not a numbered section. Did you know that MS-Word can generate a table of contents automatically?- Abstract
- Problem Statement
Should include the following:- Description of problem
- User interface
- Constraints on implementation
- Design and Method
Should include the following:- Top-down discussion of design
- Class definitions
- Major functional blocks
- Special C++ features used
- Problems encountered and solutions thereto
- Test Plan
and Test Results - Cost Analysis
In this case, just give a breakdown of hours worked. - Conclusions
What did you learn from this lab project? - References
Include any websites or other sources of pseudo-code, language references, etc.
- Abstract
Source Code Standard
Contents:
Sources:
This documentation standard is derived from some previously existing standards:
Firmware Standards Manual found at http://www.ganssle.com/misc/fsm.doc
Documentation Format found at
http://www.cs.wpi.edu/Help/documentation-standard.html
C# Coding Style Guide found at
http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=336
Motivation:
The motivation for this standard is that its adoption should:- Encourage professionalism in our students;
- Set uniform course expectations;
- Improve the quality of software from our graduates;
- Improve the quality of software from our project students;
- Predispose students to accept the concepts presented in software courses;
- Make grading more uniform and easier.
Modules
General
A Module is a single file of source code that contains one or more functions or routines, as well as the variables needed to support the functions.
Each module contains a number of related functions. For instance, an A/D converter module may include all A/D drivers in a single file. Grouping functions in this manner makes it easier to find relevant sections of code, and allows more effective encapsulation.
Encapsulation — hiding the details of a function's operation, and keeping the variables used by the function local — is absolutely essential.
Modules tend to grow large enough that they are unmanageable. Keep module sizes under 1000 lines to insure tools (source debuggers, compilers, etc.) are not stressed to the point they become slow or unreliable, and to ease searching.
Templates
To encourage a uniform module look and feel, create module templates. Use one of these files as the base for all new modules. The module template includes a standardized form for the header (the comment block preceding all code), a standard spot for file includes and module-wide declarations, function prototypes and macros. The templates also include the standard format for functions.
Variables
Names
Regardless of language, use long names to clearly specify the variable's meaning. If your tools do not support long names, get new tools.
Separate words within the variables by underscores. Do not use capital letters as separators. Consider how much harder IcantReadThis is on the eyes versus I_can_read_this.
The ANSI C specification restricts the use of names that begin with an underscore and either an uppercase letter or another underscore (_[A-Z_][0-9A-Za-z_]). Much compiler runtime code also starts with leading underscores. To avoid confusion, never name a variable or function with a leading underscore.
These names are also reserved by ANSI for its future expansion:
| E[0-9A-Z][0-9A-Za-z]* | errno values |
| is[a-z][0-9A-Za-z]* | Character classification |
| to[a-z][0-9A-Za-z]* | Character manipulation |
| LC_[0-9A-Za-z_]* | Locale |
| SIG[_A-Z][0-9A-Za-z_]* | Signals |
| str[a-z][0-9A-Za-z_]* | String manipulation |
| mem[a-z][0-9A-Za-z_]* | Memory manipulation |
| wcs[a-z][0-9A-Za-z_]* | Wide character manipulation |
Global Variables
All too often C++ and especially assembly programs have one huge module with all of the variable definitions. Though it may seem nice to organize variables in a common spot, the peril is these are all then global in scope. Global variables are responsible for much undebuggable code, reentrancy problems, global warming and male pattern baldness. Avoid them!
When globals are used, put all of them into a single module. They are so problematic that it's best to clearly identify the sin via the name globals.c or globals.asm.
Declarations
Number of Declarations per Line
One declaration per line is recommended since it encourages commenting. In other words,
int level; // indentation level
int size; // size of table
Do not put more than one variable or variables of different types on the same line when declaring them. Example:
int a, b; // What is 'a'? What does 'b' stand for?
The above example also demonstrates the drawbacks of non-obvious variable names. Be clear when naming variables.
Initialization
Try to initialize local variables as soon as they are declared. For example:
string sname = myObject.Name;
or
int val = time.Hours;
Functions
Regardless of language, keep functions small! The ideal size is less than a page; in no case should a function ever exceed two pages. Break large functions into several smaller ones.
Explicitly declare every parameter passed to each function. Clearly document the meaning of the parameter in the comments.
Define a prototype for every called function, with the exception of those in the compiler's library. Prototypes let the compiler catch the all-too-common errors of incorrect argument types and improper numbers of arguments. They are cheap insurance.
Preconditions and postconditions should be explicitly described.
In general, function names should follow the variable naming protocol. Remember that functions are the "verbs" in programs — they do things. Incorporate the concept of "action words" into the variables' names. For example, use read_A/D instead of A/D_data, or send_to_LCD instead of LCD_out.
Comments
Code implements an algorithm; the comments communicate the code's operation to yourself and others. Adequate comments allow you to understand the system's operation without having to read the code itself.
Write comments in clear English. Use the sentence structure Miss Grandel tried to pound into your head in grade school. Avoid writing the "Great American Novel"; be concise yet explicit… but be complete.
Avoid long paragraphs. Use simple sentences: noun, verb, object. Use active voice: "Start_motor actuates the induction relay after a 4 second pause". Be complete. Good comments capture everything important about the problem at hand.
Use proper case. Using all caps or all lower case simply makes the comments harder to read.
Enter comments in C++ at block resolution and when necessary to clarify a line. Don't feel compelled to comment each line. It is much more natural to comment groups of lines which work together to perform a macro function. However, never assume that long variable names create "self documenting code". Self documenting code is an oxymoron, so add comments where needed to make the firmware's operation crystal clear. It should be possible to get a sense of the system's operation by reading only the comments.
Comment any line that is not crystal clear. There is no need to comment the obvious such as:
a++; // increases the counter
return; // returns from the subroutine
Such comments are a) an insult to your reader's intelligence and b) tedious to write.
Explain the meaning and function of every variable declaration. Every single one. Explain the return value, if any. Long variable names are merely an aid to understanding; accompany the descriptive name with a deep, meaningful, prose description. Explain the parameters during the function definition, as follows:
type function_name(type parameter1 /* comment */
type parameter2 /* comment */)
Though it's useful to highlight sections of comments with strings of asterisks, never have characters on the right side of a block of comments. It's too much trouble to maintain proper spacing as the comments later change. In other words, this is not allowed:
/************************************************
* This comment incorrectly uses right-hand *
* asterisks *
************************************************/
The correct form is:
/************************************************
* This comment does not use right-hand
* asterisks
*************************************************/
Single line comments must be indented to the indent level when they are used for code documentation. Commented out code should be commented out in the first line to enhance the visibility of commented out code.
A rule of thumb says that generally, the length of a comment should not exceed the length of the code explained by too much, as this is an indication of too complicated, potentially buggy, code.
Coding Conventions
General
No line may ever be more than 80 characters.
Never, ever use "magic numbers". Instead, first understand where the number comes from, then define it in a constant, and then document your understanding of the number in the constant's declaration.
Spacing and Indentation
Put a space after every keyword, unless a semicolon is the next character, but never between function names and the argument list.
Put a space after each comma in argument lists and after the semicolons separating expressions in a for statement.
Put a space before and after every binary operator (like +, -, etc.). Never put a space between a unary operator and its operand (e.g., unary minus).
Put a space before and after pointer variants (star, ampersand) in declarations. Precede pointer variants with a space, but have no following space, in expressions.
Indent C++ code using tab characters.
Always place the # in a preprocessor directive in column 1.
C++ Formatting
Never nest if statements more than three deep; deep nesting quickly becomes incomprehensible. It's better to call a function, or even better to replace complex ifs with a switch statement.
Place braces so the opening brace is the last thing on the line, and place the closing brace first, like:
if (result > a_to_d) {
do a bunch of stuff
}
Note that the closing brace is on a line of its own, except when it is followed by a continuation of the same statement, such as:
do {
body of the loop
} while (condition);
When an if-else statement is nested in another if statement, always put braces around the if-else to make the scope of the first if clear.
When splitting a line of code, indent the second line like this:
function (float arg1, int arg2, long arg3,
int arg4)
or,
if (long_variable_name && constant_of_some_sort == 2
&& another_condition)
Use too many parentheses. Never let the compiler resolve precedence; explicitly declare precedence via parentheses.
Wrapping Lines
When an expression will not fit on a single line, break it up according to these general principles:
- Break after a comma.
- Break after an operator.
- Prefer higher-level breaks to lower-level breaks.
- Align the new line with the beginning of the expression at the same level on the previous line
Example of breaking up function calls:
longFunctionCall(expr1, expr2,
expr3, expr4, expr5);
Examples of breaking an arithmetic expression:
PREFER:
var = a * b / (c - g + f) +
4 * z;
BAD STYLE — AVOID:
var = a * b / (c - g +
f) + 4 * z;
The first is preferred, since the break occurs outside the parenthesized expression (higher level rule). Note that you indent with tabs to the indentation level and then with spaces to the breaking position in our example this would be:
> var = a * b / (c - g + f) +
> ......4 * z;
Where '>' are tab chars and '.' are spaces. (the spaces after the tab char are the indent width of the tab). A good coding practice is to make the tab and space chars visible in the editor which is used.
White Spaces
An indentation standard using spaces never was achieved. Some people like 2 spaces, some prefer 4 and others die for 8, or even more spaces. Better use tabs. Tab characters have some advantages:
- Everyone can set their own preferred indentation level
- It is only 1 character and not 2, 4, 8 ... therefore it will reduce typing (even with smart indenting you have to set the indentation manually sometimes, or take it back or whatever)
- If you want to increase the indentation (or decrease), mark one block and increase the indent level with Tab with Shift-Tab you decrease the indentation. This is true for almost any text editor.
Here, we define the Tab as the standard indentation character.
Blank Lines
Blank lines improve readability. They set off blocks of code which are in themselves logically related. Two blank lines should always be used between:
- Logical sections of a source file
- Class and interface definitions (try one class/interface per file to prevent this case)
One blank line should always be used between:
- Functions
- Properties
- Local variables in a function and its first statement
- Logical sections inside a function to improve readability.