Object-Oriented Programming

Rainfall Coding Practices

Michael L. Collard, Ph.D.

Department of Computer Science, The University of Akron

Purpose

  • The following is a list of coding practices
  • We will demonstrate the violation using the starting Rainfall exercise code
  • For each violation, we will show the proper form by making changes to the Rainfall code
  • Each fixed violation is a separate git commit. The commit message will describe the purpose of the code change
  • git commit messages have a particular format and wording style. Use the messages exactly as I show in class.

Guidelines Followed in Starting Code

  • Indentation levels follow the flow of control and are consistent
  • Indentation is all spaces and not mixed spaces/tabs
  • File ends in a newline

Header Comment

Every program file needs a header comment

  • Minimally includes the name of the file
  • Minimally describes what the program does (for a program with a main()) or what the file is for
  • May include a license
  • Do not include your name (what version control is for)
  • Do not include a list of dates/changes (what version control is for)
  • May include a copyright

Side Note: License

  • For most classwork, software licenses are not used
  • However, outside of classwork, all files should contain a license
  • License may be free and open, e.g., GPL2, GPL3, MIT License, Popular Licenses
  • License may be non-free, e.g., a commercial license
  • Previous style was to include the license in the Header Comment
  • Current style is to include the license in a machine-readable SPDX comment before the header comment.

Side Note: License Previous vs. Current

Order of Include Files

Include files should be at the top of the file before any other statements

  • An include file should be able to be at any position of the list and cannot depend on previous includes
  • The order should be logical
  • Lots of ways to organize, most general first, etc.
  • Whatever the organization, order them logically not historically

Final return In main()

main() requires a return for all code paths

  • The declaration of main() has a return type of int
  • No final return leads to an undefined return value when the program runs
  • A return of 0 indicates that the program was successful
  • A non-zero return indicates that the program had a runtime error
  • Can use different positive values for different runtime errors

using namespace std;

Do not use using namespace std;

  • Causes significant problems if used in an include file (*.hpp)
  • Code is read much more often than code is written
  • Does not save time and causes potential confusion
  • Loses the grouping that the namespace prefix provides
  • To use correctly, must declare inside of the main() function

Error and Log Messages

Write error and log messages to standard error

  • standard input - AKA, stdin, std::cin
  • standard output - AKA, stdout, std::cout
  • standard error - AKA, stderr, std::cerr and std::clog
  • Write the typical output of a program to standard output
  • Write error and log output to standard error
  • std::cerr is unbuffered while std::clog is buffered

End Output

End final output of the program with a newline

  • Easy to see with a shell
  • Integrates better with other tools
  • May be options to automate this

Sections of Statements

Separate each section of statements with a single, blank line

  • A program does a wide variety of things
  • Each thing it does often requires multiple lines of code
  • Separate the sections of code with a single, blank line

Comments for Sections of Code

Every section of code needs a comment describing the purpose

  • Can be seen as pseudocode
  • Comment goes before the section of code it describes
  • Allows a developer to find the code of current interest more quickly
  • Adding the comments before the code is written serves to design the overall algorithm

Declare Variables Individually

Declare only one variable in a declaration statement

  • Just because variables have the same type, it does not mean that they should be declared together
  • Types (and positions) of where variables are declared can change as the program progresses
  • A single declaration allows room for comments
  • With pointers, references, and modifiers, easy to make mistakes, e.g.,

Standard Number Types

Prefer int and double

  • int is the type of a literal integer, e.g., 123
  • Use int as the standard C++ integer type. Only use long e.g, 123l, unsigned int e.g., 123u, and unsigned long long e.g., 123ull if specifically needed. Possible exceptions are size_t, std::size_type
  • double is the type of a literal floating-point value, e.g., 1.23
  • double is the type for all of the standard math functions, e.g., log()
  • Use double as the standard C++ floating-point number type. Only use float if specifically needed, e.g., SSE instructions

Declarations of Local Variables

Initialize local variables where they are declared

  • Purpose of temporary variables is to store a result for further use in the program
  • Declaring them right where needed puts them in the correct section
  • The "declare all variables first" is an old style from the C-language
  • Always initialize temporary variables with a proper value

Variable Names

All variable names should be descriptive of what they store

  • Naming is one of the hardest problems in any software design
  • Single-variable names for general purposes lose the documentation aspect of a name
  • We will discuss naming in more detail later on

auto Keyword

Use auto when initializing a declaration that includes a non-literal expression

  • Adds flexibility if types change
  • Exception: Use an explicit type instead of auto when initializing with a literal value to make certain you get the intended type

Cohesive Sections

Put the calculations of each result in a separate section

  • If calculating more than one result at a time, put in separate sections of code
  • Concentrate on functionality first to organize the program, then on statements needed
  • Students often overestimate the computation cost of the loop structure

Expressions in Output Statements

Use only simple expressions in output statements

  • Separate output from calculation
  • Simple expression: Single variable or literal
  • Simple expression: Single call

Loop Traversal

Prefer range-based for over indexing

  • The for statement with indexing can lead to an infinite loop

  • Range-based for is safer and does not lead to infinite loops with standard containers

  • Extension of using iterators:

const Variables

Use the const specifier on a type whenever possible

  • Always use when a variable is initialized in the declaration with its final value
  • You may find that a variable that you believe is not changed later on is changed later on

C++ Standard Library

Prefer std::istream_iterator over an input loop

  • Look for existing algorithms in the standard library of C++ instead of writing your own loop
  • Use in declarations often allows the use of const
  • Just one of many examples
  • Use of other libraries, e.g., boost, may cause problematic dependencies and depends on the project restrictions
  • In this class, only use the standard libraries of C++ unless told otherwise

Extract Method

Create new functions for sections of code with loops and multiple calculations

  • Official name is the "Extract Method" refactoring
  • Creates an abstraction by hiding explicit code behind a name and parameters
  • Abstractions have a cost, so whether to extract or not is a design decision, and is not always a straightforward decision
  • E.g., don't extract the // output the rainfall report because that code belongs to main()

C++ Standard Library

When performing a standard operation on a container, look for existing solutions

  • C++ Standard Library is full of algorithms to work on the standard containers
  • Look for those algorithms instead of creating your own
  • Replacing your custom code with a general solution is a win
  • Good developers are happy when they can delete code

Cost of Abstraction

If a function is not doing very much, consider replacing the call with the calculation

  • Official name is the "Inline Method" refactoring
  • By "inline," we do not mean the inline specifier, which often has no effect at all
  • BTW, the inline specifier is banned in this class
  • Only do this with straightforward calculations
  • Would only rarely do this for a calculation that involves a loop
  • Design is iterative, so as the program is further developed, it is not unusual to undo previous design decisions