![]()
|
© 2000, Christopher Newport University. |
This document describes the format that shall be used in all programming assignments involving separate compilation.
Separate compilation refers to the fact that your C++ program is broken up into more than one file (compilation unit). Each program will have the following files:
The files should be kept in a single directory. Keep all project files in that directory. Keep NOTHING ELSE in that directory. The following sections show examples of how to format each of the different files.
The Makefile describes the dependencies among your compilation units. Here is an example of an acceptably documented Makefile:
# TITLE: Makefile # AUTHOR: AC Siochi # DATE: FA96 # PURPOSE: Makefile for str255 class and test driver # DESCRIPTION: str is the executable image. related files are str255.h # str255.C, main.C (driver). clean target allows for deletion of object # code and executable. str: main.o str255.o g++ main.o str255.o -o str main.o: main.C str255.h g++ -c main.C str255.o: str255.C str255.h g++ -c str255.C clean: /bin/rm -f *.o str # keep a single blank line at the end of the Makefile
|
Notice that the Makefile also has a documentation header, similar to that used in the client.
WARNING!!! Be sure that there is a blank line AFTER the last line of your makefile. There is a defect in the make program which is activated if this blank line is missing (the last command of your makefile will not work!)
WARNING!!! The comment character for makefiles is the pound sign (#), not the double slash (//)
The driver or client is the program that uses the objects described in the specification file. When the driver is compiled, its object code must be linked to the object code of the implementation file. What follows is an example of an acceptably documented and formatted client:
// TITLE: main.C
// AUTHOR: AC Siochi
// DATE: FA96
// PURPOSE: Test the str255 class
//
// DESCRIPTION:
// Instantiate three str255 objects. Set values of two of them to "hi"
// and " There". Print them to confirm this works. Perform tests of ==
// and < operators. Set third object to concatenation of first 2. Print
// to test.
#include <iostream.h>
#include "str255.h"
main() {
str255 s1, s2, s3;
s1.setTo("Hi"); s1.print(); cout <<endl;
s2.setTo(" There"); s2.print(); cout <<endl;
//ASSERT: "Hi" <return>" There" should appear on cout.
if (s1 == s2)
cout << "s1==s2\n";
else if (s1 < s2)
cout << "s1 < s2\n";
else
cout << "s1 > s2\n";
//ASSERT: Since "Hi" is alphabetically greater than " There",
//"s1 > s2\n" should appear on cout.
s3 = s1 + s2;
s3.print(); cout << endl;
//ASSERT: "Hi There" should appear on cout
}//main
|
The specification is the file that describes the capabilities of a class. The specification only states WHAT the class can do, it does NOT define HOW the class does it. What follows is an example of an acceptably documented and formatted specification:
// AUTHOR: AC Siochi
// TITLE: str255.h
// DATE: FA96
// DESCRIPTION: Specification of str255 class. Provides strings of max
// length 255.
class str255 {
// INV: last char of array is \0
private:
char str[256];
public:
str255();
//CTOR
//POST: self.str == ""
int operator==(str255 s) const;
// POST: FCTVAL == TRUE if s.str == self.str, FALSE else
int operator<(str255 s) const;
// POST: FCTVAL == TRUE if self.str < s.str, FALSE else
str255 operator+(str255 s) const;
// PRE: length(self.str)+length(s.str) <= 255
// POST: self.str == concatenation of self.str<entry> and s.str
void print() const;
// POST: self.str is printed to cout
void setTo(char s[]);
// POST: self.str == s
}; // class str255
|
Notice that the specification file has a documentation header that gives the author, title, date, and description. Also required are pre and post conditions for each function prototype, and comments where appropriate.
Note also the use of the term self. Self is an identifier we will use in this course that refers to the object that received the message. You might also see the term this used instead of self.
The implementation file defines HOW a class performs each of its methods. Every class therefore will have 2 related files: the specification (.h) and the implementation (.C). What follows is an example of an acceptably documented and formatted implementation:
// AUTHOR: AC Siochi
// TITLE: str255.C
// DATE: FA96
// DESCRIPTION: Implementation of str255 class
#include <iostream.h>
#include "str255.h"
#include <string.h>
str255::str255() {
// CTOR
// POST: self.str == ""
str[0] = '\0';
}//ctor
int str255::operator==(str255 s) const {
// POST FCTVAL==true if self.str == s.str, false else
return (strcmp(str, s.str) == 0);
}
int str255::operator<(str255 s) const {
// POST FCTVAL== true if self.str < s.str, false else
return (strcmp(str,s.str) < 0);
}
str255 str255::operator+(str255 s) const {
// PRE len(self.str)+len(s.str) <=255
// POST FCTVAL== concatenation of self.str and s.str
str255 catStr;
strcat(catStr.str,str);
strcat(catStr.str,s.str);
return catStr;
}
void str255::print() const {
// POST: self.str appears on cout
cout << str;
}//print
void str255::setTo(char s[]) {
// POST: self.str == s
strcpy(str,s);
}//setTo
|
Notice that the implementation file has a documentation header that gives the author, title, date, and description. Also required are pre and post conditions for each function prototype, and comments where appropriate. Note that it is NOT required that every function definition in an implementation file have a documentation header. What is required is that each function be easily located (good use of formatting and whitespace) and have pre and post conditions. Other guidelines (e.g., meaningful identifiers) still apply as well.
Note also the use of the term self. Self is an identifier we will use in this class that refers to the object that received the message. You might also see the term this used in this fashion as well.
Use whitespace (spaces, tabs, carriage returns) to reflect the semantics of your code. Notice in the above example, all statements in main() are indented, to show that they belong inside main(). Note also that blank lines are used to separate sections of code.
When choosing names for variables or constants, use names that describe the variable or constant. For example, in the source code above ave is meaningful since ave reflects the nature of the variable: it will hold the average of the ten integers.
Use interCaps when combining words to make an identifier. InterCaps refers to the capitalization of the first letter of each new word. calcAverage is an example. This is more compact than using underscores to separate the words: calc_average.
An opening brace ("{") never appears on a line by itself. It appears at the end of the if, while, or function header. The closing brace ("}") appears on a line by itself, at the same level of indentation of the code body which it closes. The closing brace shall be labelled with a comment to indicate which statement it closes. The only allowable exception for this is the closing brace of a function. In this case the closing brace may be flush with the left margin.
For example, the following is coding standards compliant:
if (c != 'x') {
cout << "not equal\n";
i++;
}//end if
However, the following is not (what are the two violations?):
if (c != 'x')
{
cout << "not equal\n";
i++;
}
"If" statements should be formatted to show clearly which are the statements comprising the true clause and the false clause. The statements of a true clause should be indented past the column of the "if" statement, and the closing } of the true clause should align with the true clause statements:
if (c != 'x') {
cout << "not equal\n";
i++;
}//end if
Note that the closing } is labelled with a comment indicating which statement it completes. In this case it is labelled //end if to show it completes the if statement. Similar indentation whould be performed for else statements:
if (c == '+') {
cout << "Add the numbers\n";
cout << num1 + num2 << endl;
}//end if
else if (c == '-') {
cout << "Subtract the numbers\n";
cout << num1 - num2 << endl;
}//end else if
else {
cout << "Unknown operation: " << c << endl;
}//end else
While statements are formatted to show the extent of the body of the loop. Formatting is similar to that of conditionals:
sum = 0;
i = 0;
while (i < 100) {
sum = sum + i;
i++;
}//end while
Functions are formatted to show the extent of the body of the function. The function header shall be flush with the left margin and the body of the function shall be indented at least two spaces. The opening brace of the function shall start on the same line as the function header. The closing brace shall be on a line by itself, flush with the left margin.
The pre and post conditions shall follow the function header, indented to the same level as the function body.
Here is an example:
float CstatInt::numAve() {
//PRE: assigned(nums)
//POST: FCTVAL == average of num[0..maxNumInts-1]
int i=0;
int sum=0;
while (i < maxNumInts) {
sum = sum + nums[i];
i++;
}//end while
return float(sum) / maxNumInts;
}//end numAve
Class / struct specifications follow the same principle of using indentation to show level of code. The class header starts flush with the left margin, the private and public key words are indented one level, and the code bodies of the private and public sections are indented one level further. Note that the private section comes before the public section.
Pre and post conditions shall appear with all member functions, indented one level further.
Here is an example:
class CstatInt {
private:
int nums[maxNumInts];
public:
void getNums();
//POST: nums contains maxNumInts ints obtained from stdin
float numAve();
//PRE: assigned(nums)
//POST: FCTVAL == average of num[0..maxNumInts-1]
}; // end CstatInt
Adhere to the formatting standards WHILE YOU TYPE your code and whenever you write your code, especially when by hand. Why? The standards are designed to help you WRITE as well as READ your code. When we write, we are reading what we write at the same time. We check to see that what we wrote was what we intended to write. If we write sloppily and without structure, reading becomes harder. This is especially true for complex structures such as nested whiles or ifs.
You will receive little benefit from the standards if you write your code sloppily, then "after it works" make it pretty for Dr. Siochi. The formatting is just as important for you, the writer, as it is for the reader.
One of the most difficult syntax errors to track down is unbalanced { and }s. This means that you may have an opening {, but got confused and left out a } somewhere. This especially happens if you ignore the formatting tips above. As a practical way of avoiding this problem, use the following strategy when editing your source code.
Develop compound statements OUTSIDE IN.
For example, when you type in a while loop, type the loop header and compound statement first:
while (i < 10) {
}//end while
Now you have the OUTSIDE of your while loop. You have definitely matched the opening { and the closing } of your while loop's compound statement. At this point, just insert the statements of the loop body INSIDE!
while (i < 10) {
sum = sum + 10;
i++;
}//end while
If you follow this method whenever you have compound statements, you minimize the probability that you'll forget to put the closing }. (Note this also works with parentheses.)