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.
- 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:
Suppose we have
a scenario in which the value 42 appears in multiple locations within the code,
used for different purposes in different places.
Some months later on, another developer is maintaining the code and needs to
alter that to 45 for one of those purposes but leave the others unchanged.
They now need to manually go through the code, correctly identifying and altering
exactly the correct subsets of 42.
If we had instead declared constants for each of the different purposes then
the developer would only have to make one single change in the definition of one
constant.
- 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.
- When we pass data as parameters we can see the chain of data transmission,
and we can see from its parameter list what "outside" data a function is accessing.
This information can be gleaned simply by looking at the function profiles in a
header (.h) file, with no need to dig into the content of the function itself.
- When a function accesses a global variable, however, the only way to detect
that is to search the actual function source code for the relevant access.
- If we need to debug or modify a program's behaviour and that behaviour turns
out to make use of global variables then our only recourse is to search out
all the functions accessing that variable and read all those accesses (and possibly
a significant portion of the surrounding code) to determine which ones are
actually relevant to the maintenance we wish to perform.
- There are situations in which global variables are an appropriate mechanism,
but we'll save that discussion for the software engineering course.
- 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;).