CNU

CPSC 231.SIOCHI CODING STANDARDS: Separate Compilation

© 2000, Christopher Newport University.


Comments to siochi@pcs.cnu.edu

This document describes the format that shall be used in all programming assignments involving separate compilation.

Structure of a C++ Separate Compilation Assignment

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.

Makefile

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 (//)

Driver, main.C

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 

 

Specification, header (.h) files

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.

Implementation, (.C) files

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.

General Formatting Tips

Use whitespace to reflect semantics

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.

Use meaningful identifiers

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.

Opening and closing braces

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++;
  }

Formatting Conditionals

"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

Formatting Iteration

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

  

Formatting Functions

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

Formatting Classes/Structs

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

  

Formatting Tips

Format as you type

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.

Edit from the OUTSIDE IN

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.)