CSCI 160 code standards

All code submitted as part of assignments or lab exercises is expected to adhere to the set of code standards described below. Failing to adhere to these standards can result in loss of marks for the lab or assignment in question.


execution
Your code must execute correctly on otter (csci.viu.ca), without crashes and without extraneous features, input, or output (i.e. limit your programs to the behaviour described by the lab/project specficiations).


compilation
Your code must be written in C++ and must compile cleanly - meaning no errors and no warnings - when compiled on otter (csci.viu.ca) using the -Wall -Wextra -pedantic options of the g++ compiler.

Code that generates errors/warnings is not trustworth: no warning is too small to ignore.

(Just as a heads-up, professors for many of the later csci courses will give an automatic fail to any program that generates a single warning message when it compiles, no matter how "good" the code is otherwise.)


attribution of others' work
You are free to use the following without attribution:
  - code provided in Dr. Wessels' CSCI 160 course lectures, lecture notes, labs, and assignment for this semester,
  - code you have written specifically for this semester's CSCI 160 labs or assignment.
Code and solutions from any other source (including any of your own code that does not fall under the categories listed above) must be clearly attributed in your source code.


sequence of events
The lab requirements generally provide a specific sequence in which events are to take place, including the order in which the user is prompted to supply specific values. DO NOT change the order in which the user is expected to enter data, and DO NOT add additional questions for the user to answer.

One part of the grading of your labs will often involve an automated marking tool, that provides values to your programs in the order specified by the lab - if your program expects the data in a different order then it will fail the automated testing and lose marks accordingly.


program output
All prompts for the user should give the user a clear idea of what the program is doing, and (for user input) should give the user a clear idea of the input format/range expected, e.g.
"Please enter the circle radius as a real number, e.g. 5.25"
as opposed to something like "Enter value"

All error messages should give the user a clear and concise view of what went wrong, e.g.
"Out of range value entered: 32 (should be an integer in range 1-19)"
as opposed to something like "Bad value"

All program results should be displayed in a way that explains their context, e.g.
"For the circle of radius 5, the computed circumference was 31.4"
as opposed to something like "Result 31.4"


identifiers
All identifiers (variables, constants, functions, parameters) must have clear and meaningful names, that reveal their intended purpose to the reader. For example, circleRadius is immediately clearer and more meaningful to the reader than R or cR.


comments
When deciding on an appropriate level of detail for comments, think of what is needed to make the code clear to a peer who is unfamiliar with the particular program/project. In our case, think of it as if it were being read by a CSCI 160 student from a different class - where would they need explanation to make the code clear and maintainable.
A short comment at the top of the file should give your name, the name of the program, and a brief description of the purpose of the program, e.g.
// Dave Wessels
// labex1
//
// This program reads three polynomial coefficients
//    from the user and displays an equation in the
//    form   aX^2 + bX + c 
All variables used in a program should have a brief comment indicating their purpose. In some cases a single comment can be used to describe a group of closely related comments, e.g.
   // variables to hold the three polynomial coefficients
   float a, b, c;
Logical blocks of code within the program should have a brief comment indicating their general purpose, e.g.
   // prompt the user to enter the polynomial coeffients and
   // read them in, assume they are seperated by whitespace
   printf("Please enter the degree 2 coefficient,\n");
   printf("followed by the degree 1, then the degree 0:\n");
   scanf("%g\n", &a);
   scanf("%g\n", &b);
   scanf("%g\n", &c);


layout
Line widths: no line of code or comments in the program may be wider than 96 characters.

With respect to brackets and indentation, items at a global scope should not be indented, then the contents of each new scope should be indented an additional 3 spaces (no tabs: when your files are distributed to different people using different tools, they will probably NOT expand the way you intended), then return to the previous indentation level when the scope ends, for example:
Comments should be indented to the same level as the item they are describing.
// Dave Wessels
// A short program to compute the circumference of a circle when given its diameter

#include <cstdio>

// chosen accuracy level for Pi
const double Pi = 3.1415;

// calculate and return circumference based on radius of circle
double calcCircumf(double rad);

int main()
{
   // obtain radius from user
   double radius;
   printf("Please enter the radius of a circle, e.g. 34.5\n");
   int valsRead = scanf("%lg", &radius);

   // if valid radius compute and display circumference,
   // otherwise display appropriate error message
   if ((valsRead > 0) && (radius >= 0)) {
      double circ = calcCircumf(radius);
      printf("For a circle of radius %lg, the circumference is %lg\n", radius, circ);
   } else {
      // determine the specific nature of the problem and inform the user
      if (valsRead == 0) {
         printf("Sorry, but that was not a number\n");
      } else {
         printf("Sorry, negative values (%lg) cannot be used\n", radius);
      }
   }

   return 0;
}

// calculate and return circumference based on radius of circle
double calcCircumf(double rad)
{
   // using Pi*diameter
   return Pi*(2*rad);
}


identifiers
All names used in the program (variables, functions, parameters, constants, etc) must be meaningful - i.e. they must clearly represent the purpose of the item named, and distinguish it from other named items in the program.

For example, userInitial would be a much more meaningful identifier than i, and fuelLevel would be a much more meaningful identifier than data.


booleans and logical operations
When a logical value is needed then the bool type and values true/false should be used (i.e. NOT int 1/0).


constants
With the possible exceptions of 0 and 1, there should be no hard-coded numeric or character literals within the program. Instead, these values should be replaced with the use of constants whose scope covers all uses of the value.

For example, instead of the hard-coded value 3.1415 in the statement:
circumf = 3.1415 * radius;
we would introduce a named constant, e.g.

    const float Pi = 3.1415;
    .......
    circumf = Pi * radius;
Similarly, if we have meanings associated with specific characters in the program they should be represented with constants.

For example, if we have the user enter Q to quit or C to continue, we might use:

    const char QuitCmd = 'Q';
    const char ContCmd = 'C';
    ...
    printf("Enter %c to quit or %c to continue\n", QuitCmd, ContCmd);


array sizes
To conform to ISO standards, array sizes must be constants or constant expressions, not variables.
E.g. the following is not acceptable under the standard:
void doSomething(int arr[], int size)
{
   int someLocalArr[size];  // size is NOT a constant
   ...
(This won't apply to arrays that are dynamically-allocated using the "new", "malloc", or "calloc" operators, as discussed at the very bottom of this page.)


file I/O
Each program is responsible for explicitly closing every file it successfully opened, under all circumstances.


recursion and main
To conform to ISO standards, the main routine cannot be called recursively.


functions and top-down design
Code segments that are used repetitively should be abstracted into functions to simplify maintenance and reduce code clutter.
To improve readability, lengthy sequences of code statements should also be divided into logical blocks, each of which is abstracted into a function.

For example, rather than having a function that is 100 lines long, break it into 3-5 smaller functions and call each in turn (passing data between them through the use of parameters).

An easily-followed top-down design approach should be followed.
Each function must have a prototype above the main routine and the full implementation below the main routine.
Each function prototype should be accompanied by a short comment describing its purpose/use.

The full implementation of the function should be accompanied by a more detailed set of comments.

The layout of a function is much like the layout of main, e.g.
void myfunction(int someparameter)
{
   .....
   all this is indented 3 spaces
   .....
}
Functions should not use reference parameters unless they actually need to change the value of the parameters.
If a function returns a value then it should be checked by the calling function. If no return value is necessary/used then the return type should be void.


if statements
The brackets { } must always be used with if statements, even if the statement body consists only of a single line.
It is acceptable to omit the 'else if'(s) and 'else' block entirely.
The layout/indentation format is as follows:
   if (condition) {
      // code and comments within the block
      // is indented 3 additional spaces
   } else if (condition) {
      // ...
   } else {
      // ...
   }


do-while loops
Again, the { } brackets must always be used.
the layout/indentation format is as follows:
   do {
      // ...
      // ...
   } while (condition);


while loops
Again, the { } brackets must always be used.
the layout/indentation format is as follows:
   while (condition) {
      // ...
      // ...
   } 


for loops
Again, the { } brackets must always be used.
the layout/indentation format is as follows:
   for (initialization; condition; increment) {
      // ...
      // ...
   } 


structs
Structs should be defined after your global constants but before your function prototypes, and the struct definition should be accompanied by brief comments describing the purpose of the struct and the purpose of each data field within the struct.
The layout/indentation format is as follows:
   // comment describing struct purpose
   struct StructName {
      fieldType fieldName;  // comment
      // ... etc ...
   };


break, continue
These should be used sparingly, if at all. Normal flow of control should not be violated without a compelling reason.


goto
The use of the goto statement is expressly forbidden, and will result in an immediate loss of 50% of the possible marks for the associated lab or assignment.


dynamic memory allocation
All dynamically allocated memory must be appropriately freed before the program exits - i.e. anything created using new must be deallocated using delete.

Anytime a dynamic memory allocation takes place, the location provided by the call to new must immediately be checked to ensure it is not null, and appropriate action taken if it is null.