CSCI 161 project and lab standards (code, documentation, and processes)

All code submitted as part of any project or lab is expected to adhere to the set of code standards described below. Failing to adhere to these standards can result in substantial loss of marks.


C++ Standards

The standards below are not exhaustive: students are expected to follow sound software development practices and exercise good judgement and common sense - following poor processes or generating low quality code can still result in loss of marks even if it does not violate any specific guideline listed below.

If you think you have a legitimate reason for violating one of these standards in a lab or project submission you must get the instructor's approval at least 48 hours in advance of the submission deadline.

compilation and execution
Your project code must be written in C++ and must compile cleanly (no errors and no warnings) when compiled on the pups (e.g. pup1.csci.viu.ca, pup2.csci.viu.ca, etc) using the -Wall -Wextra options of the g++ compiler.

Code that generates errors/warnings is not trustworthy: no warning is so small or insignificant that it can be ignored.

Warnings and error messages generated by your programs should be directed to the stderr output stream, not stdout (e.g. use std::cerr rather than std::cout for error messages and warnings).

All programs must run without crashes and without generating runtime warnings/errors. (You are expected to be able to tell the difference between errors generated by C++ and your programs' own output error messages!)

attribution of others' work
You are free to use the following without need for attribution:
  - code provided by the instructor explicitly for the use of the given lab or project
  - code developed by you for this year's CSCI 161 project or labs.

Code and solutions from any other source (including your own work from other courses or independently) must be clearly attributed in your code and supporting documentation.

input error checking and sanitization
Appropriate error checking should be applied to all input data, preferably as soon as possible after the data is read.

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"

comments
The goal of commenting is to clarify or explain that which isn't immediately obvious to a developer reading source code.

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 random student in Professor Kabir's 161 sections, and ask yourself "What explanation would they need to make the code clear and maintainable?".

All files should begin with comments identifying the author and providing a short overview of the file content

Comments in a header (.h/.hpp) file should assume the reader simply wishes to use the constants/variables/functions/methods described in the header file, whereas comments in a .cpp file should assume the reader is someone who needs to modify/maintain the code.

layout
Indentation is meant to provide a clear visual indication of different scopes/control blocks: when a new scope/block begins a new level of indentation starts, and when that scope/block ends we return to the previous level of indentation. Changes in indentation thus draw the developer's eye to changes in scope, making it much easier to rapidly and accurately scan code blocks.

Similarly, blank lines in a file are used to provide clear separation between logically distinct code segments - again providing clear visual cues that allow a developer to rapidly scan through code segments.

You are expected to use clear and consistent indentation, preferably either three or four spaces, for all blocks within your code.

Wrapped lines in an edit window tend to interfere with a developer's ability to rapidly scan code blocks (as per the notes on indentation), so teams tend to agree upon a fixed maximum line width to use in source code. For this course, no line of code or comments in the program may be wider than 96 characters.

identifiers
The choice of names used in identifiers (variables, constants, functions, parameters, classes, etc) is crucial when trying to create code that is easy to read and understand.

The name should reflect the specific purpose of that item in the context of the current code block - keeping in mind that what seems obvious to you when writing the code may not be obvious to a later reader (whether that is you months/years from now, or another developer).

You may chose any naming convention you wish (subject to the limitations below), but you must use it consistently within your project.

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.

constants
The use of literal (hard-coded) values within a program can significantly increase the difficulty of subsequent program maintenance:

For this course, with the possible exceptions of 0, 1, and 2, 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);

variables
The use of global variables in any lab or project for this course is strictly prohibited, and each individual use will result in a significant loss of marks.

Explanation: global variables (especially when dealing with multiple developers and multiple files) tend to vastly complicate debugging and maintenance of code.

file I/O
While our programs tend to be fairly short (both in code and in runtime duration), more advanced programs can involve large a large number of functions and methods, spread across many files and running over a large period of time.

Having a file open multiple times simultaneously is risky if any of those accesses involve writing to the file. It is thus important that each time we open a file we also include the appropriate code to close it - preferably at the earliest suitable opportunity. program is responsible for explicitly closing every

For this course, we will require that every attempt at opening a file be suitably checked for success, and that every successfully opened file be explicitly closed prior to the end of the program.

functions, methods, 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.

An easily-followed top-down design approach should be followed.

Each function must have a prototype in the associted header file.

The full implementation of the function should generally be accompanied by a more detailed set of (implementation specific) comments than the call-focused comments in the header file.

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 that value should be meaningful and should be checked appropriately upon return.

goto, break, continue
The use of goto statements is expressly prohibited for the labs and projects for this course.

Most control (loops, if/else, switches, function calls/returns) in a programming language follows a consistent logical structure, one that the developer is used to and expecting. Goto statements deliberately violate that structure, often jumping from one control structure to another, often making it far more difficult for the developer to follow the program logic.

As with most language constructs, there are situations in which their use is effective and appropriate, but we'll leave discussion of those situations to the software engineering course.

break and continue statements also obfuscate the control logic for a loop, and should be used sparingly. Normal flow of control should not be violated without a compelling reason.

array sizes
The C++ standard (and thus many compilers) don't support the use of variables in the declaration of statically-allocated arrays.

Thus for this course array sizes (for statically-allocated arrays) 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.)

dynamic memory allocation
Memory leaks (failure to properly deallocate memory that has been dynamically allocated) are a significant source of problems in many large programs.

As such, for this course all dynamically allocated memory must be explicitly and 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.

header files
Header files provide a place to encapsulate the declarations for data types, functions, and constants, along with comments describing their appropriate use.

All .cpp files should have a matching .h file

Header files must always have appropriate guards (#ifndefs or #pragma once)

All type definitions (including enums, structs, classes, etc) belong in a .h file

All function and method prototypes belong in a .h file

No variable declarations should appear in a .h file

class definitions
All classes must declare a default constructor and destructor

Classes must use an appropriate division of public, protected, and private, and should limit the use of public accessors

namespaces
To limit the "pollution" of the name space being used, for this course do not globally use namespaces (e.g. avoid "using namespace std;"), instead either identify the specific elements needed from the namespace (e.g. "using std::string;") or specify the namespace with the element in the code (e.g. std::string s;).


*** MAKEFILE SPECIFIC STANDARDS ***

wildcards
Avoid the use of wildcards in remove commands (e.g. things like "rm -f *.o"), as these can lead to the unintentional removal of other files. This is particularly true in projects involving multiple developers, but is a habit we want to introduce now.


*** DOCUMENTATION SPECIFIC STANDARDS ***

READMEs
Each submitted lab and project must have a README file in its root directory, and that README file should describe:

grammar, spelling
The use of correct spelling, punctuation, and grammar is expected in all supporting documentation for the project.


*** VERSION CONTROL (git) SPECIFIC STANDARDS ***

version control
One of the benefits of using a version control tool like git is that we can isolate and track individual sets of changes to the code base and associate a set of notes describing the purpose/nature of those changes. This can be a substantial aid to developers performing subsequent debugging/maintenance on the code.

Commit regularly, providing clear and informative messages with each commit.

Each commit should represent a small, logically cohesive unit of change. (Avoid adding multiple different elements of functionality, or multiple different bug fixes, into the same commit.)

The code in each commit should adhere to standards (i.e. follow standards as you write the code, don't retrofit to follow them later).

Avoid indiscriminate use of "git add .", as this tends to result in the tracking and submission of an accumulation of "junk" files
instead, specifically identify the files to be added. ("Junk" files include things like the .swp files generated by editors, the .nfs files generated by browsers, various temporary files you create for experimentation while working on the lab/project, etc)