coding conventions for c++ and java applications - macadamian ...  · web viewif your application...

70
Coding Conventions and Best Practices (Sept. 2009) University of Houston College of Optometry Core Programming Module Coding Conventions and Best Practices Date: Sept 29, 2009 Hope M. Queener Introduction The following set of coding conventions is used by the Core Programming Module at University of Houston College of Optometry (UHCO). The Core Programming Module serves UHCO principal investigators by developing applications that facilitate scientific research. The applications may perform data acquisition, image acquisition and analysis, signal analysis, psychophysics control or other data analysis. The programming languages primarily used by the developers in the Core Programming Module are The Math Works MATLAB, Microsoft (MS) C++ (version 6.0 with MFC and Visual Studio .net 2005), MS C#, MS Visual Basic and Sun Java. The following set of conventions is an attempt to conform to common programming practices in the software development industry. They are based on experience of the Core programming team and on standards promoted by Microsoft, The Math Works and various developer forums. Developers at UHCO, whether or not they are directly associated with the Core Programming Module, are encouraged to adopt these practices. In this development environment, programming of a single application is generally coded one developer at a time, but may be passed on to several developers as time passes and the application grows and changes. Occasionally, two developers will be contributing to a single application

Upload: others

Post on 25-Dec-2019

1 views

Category:

Documents


0 download

TRANSCRIPT

Coding Conventions and Best Practices (Sept. 2009)

University of Houston College of OptometryCore Programming Module

Coding Conventions and Best Practices

Date: Sept 29, 2009Hope M. Queener

Introduction

The following set of coding conventions is used by the Core Programming Module at University of Houston College of Optometry (UHCO). The Core Programming Module serves UHCO principal investigators by developing applications that facilitate scientific research. The applications may perform data acquisition, image acquisition and analysis, signal analysis, psychophysics control or other data analysis. The programming languages primarily used by the developers in the Core Programming Module are The Math Works MATLAB, Microsoft (MS) C++ (version 6.0 with MFC and Visual Studio .net 2005), MS C#, MS Visual Basic and Sun Java.

The following set of conventions is an attempt to conform to common programming practices in the software development industry. They are based on experience of the Core programming team and on standards promoted by Microsoft, The Math Works and various developer forums. Developers at UHCO, whether or not they are directly associated with the Core Programming Module, are encouraged to adopt these practices.

In this development environment, programming of a single application is generally coded one developer at a time, but may be passed on to several developers as time passes and the application grows and changes. Occasionally, two developers will be contributing to a single application simultaneously. These conventions aim to help developers to organize their code and facilitate maintenance as code is handed off to other members of the labs, colleagues at other institutions and junior developers. These conventions also assist the coding and development process for the sole developer, by providing consistent style throughout projects.

This document begins with an overview section of best practices for all languages, followed by specific coding conventions for MATLAB, C++, C#, Visual Basic and Java. The final pages include the appendices of third-party documents on language conventions and references.

Coding Conventions and Best Practices (Sept. 2009)

Table of Contents

1 Best Practices for All Languages________________________________11.1 General Source Code Organization.....................................................................1

1.1.1 Use source code file names that reflects the content of the file...................11.1.2 Avoid spaces in file names..........................................................................21.1.3 Avoid changing names of source code files to reflect versions...................21.1.4 Avoid hard-coded path names in source files..............................................21.1.5 Develop and test as a least privileged user..................................................31.1.6 Maintain only project source files in the source code folder.......................31.1.7 Maintain backup development directories...................................................31.1.8 Back up files to another device or backup media........................................41.1.9 Create an installer file for your application.................................................4

1.2 General Naming Conventions..............................................................................51.2.1 Use full, meaningful names for variables and functions..............................51.2.2 For variable names choose adjectives and nouns........................................51.2.3 For variable names and function arguments use “camel” notation..............51.2.4 For function names, use a strong verb followed by an object.....................61.2.5 For function or method names, use title (Pascal) case.................................71.2.6 Boolean variables start with “is”..................................................................71.2.7 Name Boolean return arguments as true if the result is successful..............71.2.8 Name Boolean selection variables as “Selected” or “On”...........................71.2.9 Name constants using all capitals and underscores.....................................81.2.10 Use only well-accepted acronyms or abbreviations and do so sparingly.. . .81.2.11 Include units in variables representing physical quantities..........................81.2.12 Use single-character variables sparingly and with clear purpose................91.2.13 Avoid arbitrary abbreviations in variable names.........................................91.2.14 Use “temp” in a variable only if it is truly temporary..................................91.2.15 Avoid the use of underscores in variable or function names.....................101.2.16 Avoid “Hungarian Notation”.....................................................................101.2.17 Avoid hard-coded path names in source files............................................10

1.3 General Programming Conventions...................................................................111.3.1 Use static variables or constants rather than hard-coding values..............111.3.2 Avoid global variables...............................................................................111.3.3 Use loop or index variables consistently in your program........................111.3.4 Check all possible conditions in a switch/case statement..........................121.3.5 Use break or continue instead of goto statements......................................121.3.6 Initialize all variables at declaration or constructions................................121.3.7 Declare one variable per line.....................................................................131.3.8 Maintain a constant layout style................................................................131.3.9 For long expressions, put separate conditions on separate lines................141.3.10 Compare numeric values from lowest to highest to mimic math

expressions.................................................................................................151.3.11 Indent argument lists for functions that wrap to the next line...................151.3.12 When you want to copy and paste, consider redundancy..........................161.3.13 When you want to copy and paste, consider a new function.....................161.3.14 Aim for source code modules of 1000 lines or less...................................16

Coding Conventions and Best Practices (Sept. 2009)

1.4 General Source Code Documentation................................................................191.4.1 Include an introduction comment at the top of the source file..................191.4.2 Include copyright information...................................................................191.4.3 When declaring variables, add comments on the same line......................191.4.4 Comment on the purpose of a conditional block of code or loop..............201.4.5 Describe each routine briefly at the top of the routine...............................201.4.6 Document the source of algorithms that are used......................................201.4.7 Comment on the routine's limitations........................................................211.4.8 Document flags to the bit level..................................................................21

1.5 General Error Checking and Debugging............................................................231.5.1 Check the arguments to a function or procedure.......................................231.5.2 Check preconditions and postconditions...................................................231.5.3 Use messages to signal code that should never be executed.....................231.5.4 Detect and handle an error that affects the rest of a routine......................231.5.5 If an error is detected in a routine, consider notifying the caller...............231.5.6 Remember that error handling code is vulnerable to errors.......................231.5.7 Avoid using a returned argument for both error and result.......................241.5.8 Remove the error checking code that hinders performance.......................241.5.9 Retain error checking that can assist the user............................................241.5.10 Use meaningful, polite and grammatically correct messages....................241.5.11 Reserve assertions for debugging purposes...............................................251.5.12 Test subsystems with parallel algorithms in debug versions.....................25

2 MATLAB Coding Conventions_________________________________272.1 Source Code Organization.................................................................................27

2.1.1 Use functions rather than scripts................................................................272.1.2 Use separate m-files for generic operations...............................................272.1.3 Keep specific functions in the same m-file that calls them.......................272.1.4 Add only general toolboxes to the MATLAB path...................................27

2.2 Naming Conventions.........................................................................................272.2.1 When using GUIDE, rename the tag property of each control..................282.2.2 When using GUIDE, retain the automatic function names........................282.2.3 When using GUIDE, use the handles structure carefully..........................28

2.3 Programming Conventions................................................................................282.3.1 Remember that MATLAB was developed to do math..............................292.3.2 Remember that for-loops in MATLAB differ than for-loops in C............29

2.4 Source Code Documentation.............................................................................292.5 Error Checking and Debugging.........................................................................29

3 C++ Coding Conventions______________________________________313.1 Source Code Organization.................................................................................31

3.1.1 Match the name of the class to the name of the source files......................313.1.2 Name the header files with care so that they don’t conflict.......................313.1.3 Use appropriate extensions for source files...............................................313.1.4 Avoid hard-coded path names in source files............................................323.1.5 Include only those header files which are required...................................323.1.6 Declare functions and variables only in header files.................................323.1.7 Use the MFC Version Resource................................................................32

Coding Conventions and Best Practices (Sept. 2009)

3.1.8 Build debug and release executables to their respective subdirectories....323.2 Naming Conventions.........................................................................................33

3.2.1 Specify member variables of a class as “private” and follow camel notation......................................................................................................33

3.2.2 Consider a special prefix for member variables........................................333.2.3 Name static member variables using all caps............................................333.2.4 Prefix pointer variables with lowercase “p”..............................................343.2.5 Use get and set property methods to access member variables.................34

3.3 Programming Conventions................................................................................343.3.1 Use static member variables or constants rather than hard-coding

numerical values........................................................................................343.3.2 Terminate Case Blocks..............................................................................353.3.3 Declare variables wherever they are used..................................................353.3.4 Initialize variables wherever they are declared..........................................353.3.5 Place new and delete statements so as to emphasize their relationship with

each other...................................................................................................363.3.6 Be vigilant about not accessing the pointer to an object which has been

deleted........................................................................................................373.3.7 Avoid the use of macros unless they are absolutely necessary..................373.3.8 When nesting conditions and loops, use curly braces to clarify the

statements belonging to each condition.....................................................383.3.9 Conditionals in the “if” statement should be free of assignment operators.

....................................................................................................................383.4 Source Code Documentation.............................................................................393.5 Error Checking and Debugging.........................................................................39

3.5.1 Throw an exception only when required...................................................393.5.2 A class should be left in a defined state in case of failure.........................40

4 C# Coding Conventions_______________________________________414.1 Source Code Organization.................................................................................414.2 Naming Conventions.........................................................................................414.3 Programming Conventions................................................................................414.4 Source Documentation.......................................................................................414.5 Error Checking and Debugging.........................................................................41

5 Visual Basic Coding Conventions_______________________________435.1 Source Code Organization.................................................................................435.2 Naming Conventions.........................................................................................435.3 Programming Conventions................................................................................435.4 Source Documentation.......................................................................................435.5 Error Checking and Debugging.........................................................................43

6 Java Coding Conventions_____________________________________456.1 Source Code Organization.................................................................................456.2 Naming Conventions.........................................................................................456.3 Programming Conventions................................................................................456.4 Source Documentation.......................................................................................456.5 Error Checking and Debugging.........................................................................45

Coding Conventions and Best Practices (Sept. 2009)

Coding Conventions and Best Practices (Sept. 2009)

1 Best Practices for All LanguagesThis section describes the general best practices for programming for the University of Houston College of Optometry, regardless of the programming languages. Cases from different languages are presented in examples to illustrate ideas, but the principles transfer to all languages. The reader can infer the language of each example from the syntax for the comment.

spatialFrequencyCpd=1.0; % percent sign for MATLABdouble spatialFrequencyCpd=1.0; // double-slash for C++, C# or JavaDim spatialFrequencyCpd As Double = 1.0 ' single-quote for VB

Acknowledgements

This set of best practices was originally compiled by Raja Nadar, a UHCO employee. It is heavily based on coding conventions described by Macadamian Technologies Inc. which is a software engineering and consulting firm based in Ottawa, Canada. This set of conventions is also heavily influenced by a book titled Industrial Strength C++ by Mats Henricson, Erik Nyquist and Ellemtel Utvecklings. Other best practices have been inspired by observing common difficulties faced by novice programmers when Hope Queener supervised their projects.

1.1 General Source Code Organization

1.1.1 Use source code file names that reflects the content of the file

Use descriptive source code file names, such that the name reflects the unique content of the file. Source code file names are the first step in maintaining software. If the file name is ambiguous or cryptic, it can be difficult for a developer to see the purpose of the file when sharing or passing on code. The goal for the file names in a project is to present a general organization that is easily seen by looking at the contents of the source code folder. Sometimes, legacy file names are short because of the early limitations on file name length to 8 characters. This is true for much of the MATLAB library, and many file names used in the MFC library. Developers are no longer limited in this manner.

Consider for example a psychophysics program that performs a contrast sensitivity measurement that has the following separate modules:

a user interface module for entering experiment information, including the test condition parameters

a module responsible for drawing the visual stimuli a module responsible for controlling the sequence of the stimulus presentation and

response collection a module that gets the response from the user interface device (button box) a module that writes the results to a text file

1

Coding Conventions and Best Practices (Sept. 2009)

The following MATLAB file names would describe the organization above concisely.

CsfGui.mCsfDrawStimulus.mCsfExperiment.mButtonResponse.mCsfResults.m

Note that the module “ButtonResponse” does not have Csf in the file name. This is because the module is not unique to the Csf experiment. It is a module written for general use with any experiment that needs the button box responses.

An example of vague or cryptic naming for the above modules would be:

main.mdraw.mmyexpt.mbutton.mfile.m

1.1.2 Avoid spaces in file names

As much freedom as current developers have in naming source files, spaces in file names are not necessary and by convention, discouraged. Use mixed case to separate words in filenames. Underscores can be used sparingly to visually separate information in a folder name (such as an archive folder), but keep in mind that they are a bit awkward to type.

1.1.3 Avoid changing names of source code files to reflect versions

Maintain the same names of the source files in the project throughout the lifetime of development. Avoid names like “My”, “Final”, “Current”, “Recent” in file names and folder names. Add annotations regarding version or author to archived files, not to current project files.

SignalAnalysis.cpp // recommendedSignalAnalysisNew.cpp // avoidSignalAnalysisFinal.cpp // avoidSignalAnalysisHMQ.cpp // only in archive folder; delete from project

1.1.4 Avoid hard-coded path names to other source files

This convention applies to other source code files that a source files accesses. In general, set up the programming environment to be able to find the file without the path name being named explicitly in the source files. For example, if in a C++ file, a header file for a library function is used in an “#include” statement, the path to the header file should be defined in the Visual Studio project settings that control how the application is compiled rather than in the source file. For a MATLAB project, include toolbox folders in the MATLAB path or temporarily set the path to include folders that are not in a toolbox.

2

Coding Conventions and Best Practices (Sept. 2009)

#include <DcamSdk.h> // recommended; uses project settings#include "C:\Program Files\DCAMSDK\Include\DcamSdk.h" // avoid

Typically these library files (or toolbox files) are located in a default folder that was selected by the library installer program. If the project is copied to another developer’s system, the library files are expected to be in the same folder location on either system.

Sometimes, source files are organized in parallel folders within a project. For example, perhaps two projects share a set of files that are in a third folder. Imagine such a folder structure:

C:\...\Project\DiagnosticToolsC:\...\Project\CommonC:\...\Project\UserApplication

In this case, the projects located in the folders “UserApplication” and “DiagnosticTools” both use files located in “Common”. In this scenario, it is acceptable to use relative path notation to the files in the folder “Common”, with the assumption that these folders will retain their relative organization. This method can be easier than redefining all the project settings, which are typically full path names, when switching from one developer’s folder to another. Note that if the project structure changes, the source code will also have to be changed to reflect the new folder structure, so this method is an option, but not always the easiest to maintain.

#include "..\Common\ResultFile.h" // recommended; refer to peer folder

1.1.5 Develop and test as a least privileged user

Running as an administrative user routinely is not recommended from a security point of view. Since any of the users of software that we develop may be restricted users, our code must be usable by restricted users. Restricted users do not have write permission in the Program Files folder under Windows XP. Legacy code may have stored configuration files, which the user needs to write to the application folder (i.e. the “current folder” if no path name was specified) which is typically in the Program Files folder:C:\Program Files\application-name.

If the developer does not test the code as a least privileged user, these kinds of restrictions go unnoticed during the development cycle. Also, running as a restricted user helps to insulate one’s computer system from malicious code, which may launch as the current user and then be unable to write to the system folders. For limited use, the OPT domain has a generic user name, “developer”, that can be set up as an administrative user with domain access on your development computer. Also, one can run a single application as an administrator, while continuing to be logged on as a restricted user, using the “Run As” function.

1.1.6 Maintain only project source files in the source code folder

3

Coding Conventions and Best Practices (Sept. 2009)

One goal in maintaining source code archives is to keep the development folders compact and relevant. To support this goal, keep development folders free of obsolete source files, old copies of current source files, large data sets, etc. Any notes or test results that the developer wants to keep with the source files should be maintained as text or MS Office files that are clearly distinguishable from the source code modules. Sample data and results used by the application are best stored in a separate folder, so that archives do not fill up with images, movie files or other large data that is already backed up.

1.1.7 Maintain backup of development directories

Maintain backup of development directories either by using a version control system, or manually by retaining copies of the source code. One recommended version control system is the open source package “Subversion”: http://subversion.tigris.org/

For Windows developers, TortoiseSVN provides a convenient way to work with Subversion: http://tortoisesvn.tigris.org/

On any given day, a project may seem to move backwards instead of forwards. It can be time-saving to be able to start over from yesterday’s work when today’s progress is going poorly. Or, it may be useful to simply compare files between yesterday and today. It is also valuable to be able to recreate exactly what the code was doing during a particular release period. No code is “bug-free.” As issues are discovered, knowing when the issue was present can be important in determining how to salvage data collected during the bug-ridden time period. As to frequency of saving of versions, the rule of thumb is daily or after any milestone is reached.

Even with a version control system in place, it is also recommended to maintain backup copies of the code that you release to users (or for your own use for real data collection) on separate media such as an external backup drive or CD or DVD.

If a version control package is not available, make regular back up copies of the source folder. A preferred strategy is to copy the entire source code folder to the archive folder and append the folder name with a date code (as “_yyyymmdd”). This strategy is preferred to just saving the ‘changed’ files, because it is easy to lose track of how several modules work together. If the entire folder is saved, the entire project can be reconstructed at any point in time. If saving mid-day after a development milestone one can append “a” for an early save and then later use “b” for the end of day backup. Alternately, one might append notation that identifies the milestone. All folder notations are added only to the archive folder names, never in the current working folder. Keep the working folder clean.

To reduce bulk in archive folders resulting from Visual Studio projects, it can be helpful to delete the temporary files such as the Debug folder, the .pch (pre-compiled header files) files or .ncb files.

4

Coding Conventions and Best Practices (Sept. 2009)

As an example of the manual naming strategy, consider a psychophysics program called “RSVP” (rapid serial visual presentation) used to measure reading speed under various conditions. The proposed directory structure could be:

Working folder: C:\...\RSVPMid-day backup: C:\...\RSVP_Archive\RSVP20060622.aEnd-of-day day backup: C:\...\RSVP_Archive\RSVP20060622.bNext day backup: C:\...\RSVP_Archive\RSVP20060623First release backup: C:\...\RSVP_Release\RSVP20060419Second release backup: C:\...\RSVP_Release\RSVP20060612

1.1.8 Back up files to another device or backup media

HARD DISKS DIE! Please do not assume that yours will not. At this writing, no backup server is in place for the Core Programming module. Use CDs, DVDs, external hard drives or other separate media for backups. Do NOT use the “Opt_Public” folder as a backup device. It is intended for moving data between computers conveniently, not for long-term storage.

If one maintains clutter-free source directories and deletes the intermediate files (especially the .pch or .ncb files), a CD can hold many backups of a project. It is critical to keep copies of source code for released projects, even if the releases are not quite ready for “real” data collection. If the research staff has access to a program, assume that they will collect and/or analyze some data with it.

1.1.9 Create an installer file for your application

This topic is applies to more complex projects, such as those developed by the Core Programming Module developers. When a MATLAB project consists of many m files, or a Visual Studio project requires supporting DLL files or configuration files, it can be very beneficial to create an installer package for distributing the application. For the MATLAB projects, the MATLAB compiler can be used to create an encrypted form of the application that can be distributed with the MATLAB runtime callable engine. Generally, under Windows XP, our custom C++ or C# applications are installed to the “Program Files” folder, but if your program needs any support files, such as DLLs or configuration files, the Visual Studio Installer (separate tool for 6.0, integrated in .net 2005) provides a way to put them in the appropriate directories and to be uninstalled with the Windows Installer (Control Panel: Add/Delete Programs).

If the user needs to have write-access to a file, do not put the file in the Program Files folder. Instead, install the file to the application data folder under the “All Users” folder and maintain a copy in the appropriate user’s folder at startup:

C:\Documents and settings\All Users\Application Data\application-name

5

Coding Conventions and Best Practices (Sept. 2009)

1.2 General Naming Conventions

The naming conventions below assume that a developer has full control over the modules in the project. Often, we inherit a project with source files that followed some other convention or none at all.

If the conventions in existing modules were weakly enforced, developers are encouraged to make all new code follow the current conventions. If the prior conventions were uniformly enforced, developers should keep to the legacy convention, so that the entire project is consistent with itself.

It is left to the discretion of the developer whether to enforce new conventions or follow existing ones, but some convention should be followed consistently.

1.2.1 Use full, meaningful names for variables and functions

The purpose of a variable or function should be obvious from its name. The use of full, descriptive names enhances code. Many lines of legacy code contain cryptic names that require the developer to guess the information contained in the variable or the purpose of the function. Use correctly spelled, full words to describe variables and functions so that no interpretation is required. It may take more effort to type a longer, descriptive name, but with editing tools such as copy and paste and insertion editors, the work of doing so is manageable and worth the effort.

1.2.2 For variable names choose adjectives and nouns

When declaring variables (or function arguments), the quantity or object of interest can generally be described as a noun, modified by an adjective. Boolean variables are an exception and described separately, below.

backgroundStimulus=zeros(512,512); % recommendedbgstim=zeros(512,1512); % avoid; crypticappearStimulus=zeros(512,512); % avoid; verb

1.2.3 For variable names and function arguments use “camel” notation

In camel notation, the first word of the concatonated variable name is lowercase and subsequent words have the first character capitalized. Acronyms and abbreviations (used sparingly) only capitalize the first character, even if they are normally all capitals in written expressions.

double spatialFrequencyCpd; // recommendeddouble spatialFrequencycpd; // avoid

int imageHeightMm; // recommended

6

Coding Conventions and Best Practices (Sept. 2009)

int imageHeightmm; // avoid

meanRms = 0; % recommendedmeanRMS = 0; % avoid

1.2.4 For function names, use a strong verb followed by an object.

Functions and methods take some kind of action on data. Therefore, name them starting with a strong verb (action word) and the object of the action. Use the same verb for the same kind of operation. For example, use “compute” or “calculate” but not both within the same class. Be specific and include an adjective before the object name, where applicable. Some verbs cover just about any meaning. Avoid general names or vague names for functions or procedures.

ComputeZernikeCoefficients(); // recommendedSaveWavefrontImageFile(); // recommendedOpening(); // avoid (not a strong verb; too vague)HandleCalculation; // avoid; vaguePerformServices(); // avoid; vague

For functions whose main purpose is returning the current condition, use “Is” as the verb. In the example below, the purpose of the function is to ask “Is the plot file valid?” The function name does not suggest that the file was opened or read, or that data from the file is available. The function may only check to make sure that the file exists and that it starts with some specific character sequence.

function [isFileValid]=IsPlotFileValid(plotFileName) % recommended

Note that a boolean return value does not always mean that the function should start with “is”. If the function returns a boolean value to indicate the status of the action taken by the function, the verb should represent the action taken. In the example below, the action taken is to open and read the parameter file. The return status indicates whether the operation was successful.

fileStatus = ReadParameterFile(CString parameterFileName);//recommended

Avoid the following. In the example below, the purpose of the function is ambiguous. Does the function ask if the file has already been opened? Does it open the file and return a status on the opening operation?

IsOpenParameterFile(CString parameterFileName); // avoid

1.2.5 For function or method names, use title (Pascal) case

Function names are comprised of words that are concatenated together to describe the operation of the function on the available data. The name should include enough words to describe the function specifically.

7

Coding Conventions and Best Practices (Sept. 2009)

% recommendedfunction [zernikeResult] = ComputeZernikeCoefficients(centroids)

% avoidfunction [z] = computezernikecoefficients(c); %avoidfunction [zers] = compute_Zernike_coefficients(cents); %avoid

1.2.6 Boolean variables start with “is”

Boolean variables are used in two main contexts. In the first, a checkbox or other graphic user interface control selects an option as on (selected) or off. In the second context, the result of a function is Boolean, indicating the success or failure of the function. In either case, the leading “is” in the name cues the developer that the variable is true or false. Depending on the user interface development tools, other conventions may make more sense, however. The developer is encouraged to use his or her best discretion and apply a consistent practice when naming Boolean variables.

1.2.7 Name Boolean return arguments as true if the result is successful

When a function returns a Boolean value, the return status should be true when the function is successful. These variables are typically tested with “if” statements soon after the function call, to handle any subsequent dependencies. The name of the Boolean return argument should relate to the function name that produced it, but need not include the entire function name, if the meaning is easily understood. Begin these variable names with “is” and end with “Ok” or “Valid”.

isFileOk = ReadParameterFile(...); % recommendedisParameterSetValid = ValidateParameterSet(); % recommendedisError = ComputePsychometricFunction(); % avoid;

A function that was written prior to these conventions or outside of UHCO may return a true value in case of an error. If the result is truly Boolean, you can negate the result and still choose a “success” name for no error.

isCameraOpenOk = !cameraABC.Open (); % returns 0 (false) on success

If the result is zero for success and a numeric code for an error, use a different variable name such as “cameraOpenStatusCode”, and interpret all return conditions provided by the developer.

1.2.8 Name Boolean selection variables as “Selected” or “On”

Boolean variables are often necessary for user-selected options such as checkboxes. The checkbox control variable should be named consistently with the conventions of the language and development environment. Include a reference to the control type in the name of the control. For instance, “Check” or “Chk” is acceptable to append to all checkbox control names. Any variables not attached to the graphical user interface (GUI)

8

Coding Conventions and Best Practices (Sept. 2009)

that are initialized from the GUI (or a parameter file or a hard-coded default state) start with “is” followed by the “adjective”-“noun” pattern of other variables, followed by “Selected” or “On”. Avoid the user of “Enabled” in a Boolean variable name, unless referring to the enabled state of a GUI control. An option may be “on” and “disabled”, for instance, if a checkbox control is checked and the user is disabled from changing it.

isAdvancedProcessingSelected=advancedProcessingCk.value; % recommendedisFixationPointOn=TRUE; // recommended; may set default stateFixationPointEnabled=TRUE;// avoid;

1.2.9 Name constants using all capitals and underscores.

For constants, use the legacy C/C++ convention of using all capitals separating component words with underscores. This convention distinguishes static variables and constants from variables that can be modified.

const int MAXIMUM_MODES = 66; // number of Zernike modes(coefficients)

1.2.10Use only well-accepted acronyms or abbreviations and do so sparingly.

Abbreviations and acronyms in variable names are acceptable so long as they are standardized (in general or in vision science and/or optics), such as mm, cm, cpd (cycles-per-degree), MTF, PSF, RMS. When the variable is declared, spell out the abbreviation in the comments. Even though the acronym may typically be shown as all capitals or all lowercase, capitalize the first character, such as “wavefrontMtf”. Do not use an abbreviation for an entire variable name.

double spatial FrequencyCpd; // recommendeddouble wavefrontRmsError; // recommended; root mean square errorcpd; // avoidRMS; // avoid

1.2.11Include units in variables representing physical quantities

If a variable name represents a physical quantity, the name should include units as the last word in the name. This practice is especially important when using several unit spaces in a program, such as centimeters, pixels and degrees of visual angle to describe the extent of a stimulus. Common abbreviations for units are acceptable.

temperatureDegreeC = 2.8; %recommendedtemperatureRepresentedInDegreeCelsius =2.8; %avoid; excessivetemperatureDegreeF = 37.0;%recommended

temperature =37.0; %avoid – units are ambiguous

9

Coding Conventions and Best Practices (Sept. 2009)

1.2.12Use single-character variables sparingly and with clear purpose

In general, it is better to use a descriptive word such as “time” rather than “t” in an expression. However, if the single-character notation makes the mathematical notation more clear, or if the variable is used as an index, it can be appropriate. Keep in mind that MATLAB declares the meaning of many single characters such as “i” and “j” for the square root of -1.

timeStartMs = 0;timeEndMs =1000;t = [timeStartMs:timeEndMs]; % initialize time axissignal=sine(2*pi*t/timeEndMs);plot(t,signal);

1.2.13Avoid arbitrary abbreviations in variable names

Hardware and compiler limitations enforced short variable names in the early years of programming. This constraint led to cryptic variable naming practices, such as cutting words short or leaving out the vowels. Avoid this outdated practice. Using full descriptive words brings the benefit of readability and ease of interpretation. For clarity use only commonly recognized abbreviations in variable names. Select a longer variable name if it enhances clarity.

int subjectCount; // recommendedint sbjctCnt; // avoid

double spatialFrequencyCpd; // recommendeddouble spatFreq; // avoid

1.2.14Use “temp” in a variable only if it is truly temporary

When a variable is only computed for the sake of the next line of code, it can be appropriate to give it a prefix “temp” to make it clear that the variable is only used in the local situation for the purpose of computing a more permanent resulting variable. If declaring a variable name including “temp”, the variable should be used within the next couple of lines of code and not used again.

10

Coding Conventions and Best Practices (Sept. 2009)

1.2.15Avoid the use of underscores in variable or function names

In legacy code, the underscore was often used to separate words in descriptive variable or function names. Some older languages treated lowercase and uppercase as the same character. Modern languages are case sensitive, and therefore the camel and Pascal notation are used instead of inserting the underscore, which is awkward to type. Note also that identifiers beginning with an underscore or double underscore are reserved by the compiler in MS C++. If use of an underscore is warranted based on the convention of the development tools, follow the established convention.

double minimumFrequencyHz=1.0; // recommendeddouble minimum_frequency_hz; // avoid; underscoresdouble _minimumFrequencyHz; // avoid (leading underscore)

1.2.16Avoid “Hungarian Notation”

Hungarian notation attaches a type prefix to the variable. This notation was promoted for some years by Microsoft in the era of Visual Studio 6.0, but has been abandoned with Visual Studio .net. Since the .net development environment checks types rigorously, this notation is not as critical. Avoiding Hungarian notation simplifies variable naming and avoids errors introduced when the type is changed but the developer forgets to change the name. If an existing project was written with Hungarian notation, however, continue to follow the practice in legacy projects.

bool isValidFile; // recommendedbool bIsValidFile; // avoid

int primeNumber; // recommendedint nPrimeNumber; // avoid

1.2.17Avoid hard-coded path names in source files

Hard-coded path names render programs inflexible and potentially useless when the program is used on a computer that has a different folder structure. When loading a file in to your program, generally ask the user to supply the file name, use the application path (for read-only data), or use a configuration file that specifies the exact location of files. Configuration files can be initialized at installation time. If a default path must be supplied, identify the “My Documents” folder programmatically or use the “All Users” application data folder for the custom application. Be sure that a non-administrative user has write access to any path to which the program needs to write. For example, restricted users do not have access to the application folder under the “\Program Files” folder under Windows XP.

11

Coding Conventions and Best Practices (Sept. 2009)

General Programming Conventions

1.2.18Use static variables or constants rather than hard-coding values

Numerical constants like the maximum size of a data structure or mathematical constants such as (pi) should be declared as static member variables or constants. MATLAB does not provide a means to declare static or constant values, but it is still recommended that these types of values be set apart from variables by using the all-capitals naming convention.

MINIMUM_PUPIL_DIAMETER_MM=3; % recommended, used to check user input

#define PI = 3.1415926535897931; // recommended for legacy C++static double PI = acos(-1.0); // recommended for static variable

pupilAreaSquareMm = PI*pupilRadiusMm*pupilRadiusMm;//recommendeddouble pupilAreaSquareMm=3.14159*pupilRadiusMm*pupilRadiusMm;//avoid

1.2.19Avoid global variables

Global variables lead to complications that can easily be avoided by declaring all variables as members of a class or keeping the variables local to a function. In C++ or C#, use classes. In MATLAB pass data as arguments to functions and avoid using the global workspace or scripts.

1.2.20Use loop or index variables consistently in your program

When looping through the same array, always use the same index name. For example, if looping through a matrix with index variables “row” and “column”, do not later use variables “xIndex” and “yIndex”. If using “i”, “j” and “k” as iterators on a three dimensional array, always use “i” for the same dimension of the array. Use the same type of name on different dimensions.

for (int row =0; row <maximumRows; row++) // recommended{

for (int column =0; column <maximumColumns; column ++){

ShackHartmannSpotImage[row][column]= 0;}

}...ShackHartmannSpotImage [row][column] = 10; // recommended// avoid the following: different index names on same arrayShackHartmannSpotImage [yIndex][xIndex] = 10;//avoid the following: inconsistent naming on same arrayShackHartmannSpotImage [row][j] = 10;

12

Coding Conventions and Best Practices (Sept. 2009)

1.2.21Check all possible conditions in a switch/case statement.

When writing a switch-case statement, handle all possible conditions, even if a condition is not expected. The “default” case in C++ or “otherwise” case in MATLAB allows the code to handle all possible states of the switch argument. Sometimes a condition is added to the software design at a later time point and, due to human error, one of the relevant switch/case blocks is neglected when the code is revised. For instance, some option may be added to a pull-down list on the user interface that affects code several layers away in the architecture.

How the developer handles the unexpected state of the switch argument depends on the impact of the decision. It may be appropriate to throw an exception, display a warning message to the user, redirect the flow of the program, log the unexpected condition, or change the argument to a default state. For novice developers, simply displaying an error message and gracefully exiting the program can be a good way to handle this situation.

int rsvpResponse = RsvpDialog.DoModal();switch (rsvpResponse){case IDOK:

MessageBox(“Thank you for your time today.”);break;

case IDCANCEL:MessageBox(“Please come back another time.”)break;

default:MessageBox(“Unrecognized result from RSVP”);return;

}

switch lower(method) case {'linear','bilinear'} disp('Method is linear') case 'cubic' disp('Method is cubic') case 'nearest' disp('Method is nearest') otherwise disp('Unknown method.')end

1.2.22Use break or continue instead of goto statements.

The goto statement has the potential to create difficult to follow flow of logic because it permits continuation anywhere in the routine. Such arbitrary jumps in flow are never necessary and create difficult to maintain code.

1.2.23Initialize all variables at declaration or construction

13

Coding Conventions and Best Practices (Sept. 2009)

Generally, it is good practice to initialize a variable when you declare it. (In MATLAB, this concern does not apply. Variables only begin to exist when they are initialized). The purpose of this practice is to set variables to a known state as soon as they exist. This convention is especially important for variables that are used for memory addresses (pointers in C/C++), which should be assigned a NULL value until they are initialized with a valid memory address. This practice also avoids programming errors due to neglected initializations.

int stimulusLevel; // avoid; value of variable is unknown...stimulusLevel = userDefinedStimulusLevel;

// recommendedint stimulusLevel = userDefinedStimulusLevel;

1.2.24Declare one variable per line

In general, declare one variable per line and comment on its purpose on the same line. When variable types need to change, or be renamed, or deleted, this convention simplifies the editing. If two variables are strongly related it is occasionally acceptable to declare them in one line.

int cubeCount; // number of cubes (recommended)int xIndex, yIndex, zIndex; // 3D array indices (acceptable)int i, j[100], *k, *m[100]; //avoid

// C/C++: seems to declare two pointers, but 2nd is not a pointerint* pPrimeNumberVector, pOddNumberVector; // avoid

1.2.25Maintain a constant layout style.

Use curly braces consistently (if applicable). Indent consistently. The recommended styles are to put curly braces on separate lines, indented equal to the conditional statement or function declaration that initiated the block of code. The block is indented one tab. For long blocks, it can be helpful to comment at the closing curly brace to indicate the associated open curly brace. The example below shows the general style.

14

Coding Conventions and Best Practices (Sept. 2009)

void classname::CheckSomething(){

boolean startCondition=true;startCondition = DoSomething();while (startCondition){

startCondition = DoSomething();loopCondition = DoSomethingElse();

if (loopCondition){

DoSomething();switch (condition) {

case CASE_1:{

DoSomethingForCase1();break;

}case CASE_2:{

DoSomethingForCase2();break

}case CASE_3:{

DoSomethingForCase3();break

}default: {

DoSomething();break;

}}

} // loopCondition} // startCondition

}

1.2.26For long expressions, put separate conditions on separate lines.

It can be helpful to separate conditions on separate lines for readability. However, at the minimum, indent long conditions so that it is clear that the subsequent lines of code are part of the starting expression.

// the following is hard to visually parse (avoid this style)if (( calculatedNumber<= inputNumber) || ( calculatedNumber<= 100) || (inputNumber <= 100) || (inputNumber >= 150) || (calculatedNumber >= 300)){

doSomething(calculatedNumber);}

15

Coding Conventions and Best Practices (Sept. 2009)

//The following is easier to visually parse (recommended style)if (( calculatedNumber<= inputNumber) ||

(calculatedNumber <= 100) || (inputNumber <= 100) ||(150 <= inputNumber) ||(300 <= calculatedNumber))

{doSomething(calculatedNumber);

}

1.2.27Compare numeric values from lowest to highest to mimic math expressions

For comparisons within a range it is helpful to place variables and constants in increasing order to resemble the math such as (0<x<N). This expression can be written in C++ as:

if ((0<x) && (x<N))

This expression makes it clear that x is in the middle. If checking outer limits to generate an error:

if ((x<0) ||(N<x)

The above expression makes it clear that x is outside the limit. For multiple conditions, the lowest to highest logic falls through nicely into “else if” conditions.

// select direction for a rotating E stimulusdouble orientationChance = rand();int letterOrientation; if (orientationChance < 0.25)

letterOrientation = typeLeft;else if (orientationChance < 0.5)

letterOrientation = typeBottom; // 0.25 < x is already determinedelse if (orientationChance < 0.75)

letterOrientation = typeRight; // 0.5 < x is already determinedelse if (orientationChance <= 1.0)

letterOrientation = typeTop;else // this should never happen

letterOrientation = typeError;

1.2.28Indent argument lists for functions that wrap to the next line

For functions with a long list of input arguments, indent the next line(s) to show that they are associated with the function call. If grouping arguments, group associated arguments, such as an array and its length, or the height and width of a window, on the same line. It is also acceptable to put each argument on its own line, especially if commenting each argument in example code or when using long arguments names. When a routine has a large parameter list, typing it all in one single line makes the arguments hard to identify.

16

Coding Conventions and Best Practices (Sept. 2009)

If the line exceeds the typical width of the development environment window, the preferred way to layout such a function is to align the parameters to the nearby tab position. Add comments to arguments if necessary.

// example shows coordinates grouped logicallydrawWindow(int leftPixel, int topPixel,

int rightPixel, int bottomPixel,int penWidth);

CString errorMessage;errorMessage.Format(“This error occured at %d seconds after midnight”,

timeAfterMidnightSeconds);

1.2.29When you want to copy and paste, consider redundancy

When setting up a new condition in an if-statement or switch-case block, it is generally tempting to copy and paste the statements from an existing condition as a starting point when the number of statements is more than a couple of lines of code. This serves as a reminder for all the things that need to take place within this each branch of the conditional block. To implement a new condition using best practices, consider if all branches are executing any identical statements, and bring identical statements out of the conditional branches. Conditional branches should execute only the statements that are unique to each condition. Avoid redundancy in conditional branches.

1.2.30When you want to copy and paste, consider a new function

When marking a block of code to copy and paste it, consider whether the code could be moved to a separate function. What are the differences between the existing usage of the block of code and the new usage? If the differences are small, the code can most likely be implemented as a function called from both places, with the differences passed as arguments. The advantage of using functions instead of copy and paste methods is that any changes or corrections to the logic need only be applied in one place. It is important that functions created in this situation be adjusted so that the variable names within the new function are general enough to make the purpose clear. For instance, consider a block of code that was originally written to select a TIFF image file. Then, the developer decides to add the option to read bitmap image files, also. If the variables for the file name contain the acronym “tiff”, then the new function should change those variables to use a more generic description, such as “image”.

1.2.31Aim for source code modules of 1000 lines or less

Scrolling through a very large source file to find a particular function takes time. Breaking up the code into logical functional purposes greatly enhances the ease of maintenance. When a source file grows beyond about 1000 lines, it can be beneficial to evaluate whether some of the functions could be repackaged as a class or as a set of related functions in another module. User interface modules (forms or dialog boxes)

17

Coding Conventions and Best Practices (Sept. 2009)

cannot typically be broken up, due to the common event handling for all controls on the interface. Often, however the functions behind a group of menu items or pushbuttons can be separated into a module that handles operations of a particular nature.

18

Coding Conventions and Best Practices (Sept. 2009)

This page is intentionally blank

19

Coding Conventions and Best Practices (Sept. 2009)

1.3 General Source Code Documentation

1.3.1 Include an introduction comment at the top of the source file

At the very top of each source file, include descriptive information regarding the author, date, principal investigator and purpose associated with the file. Include a copyright note and “University of Houston College of Optometry”. For principal investigators, include the complete name, or at a minimum the first initial and last name. The example below is for a source file named RsvpDialog.cpp, written by Hope M. Queener for principal investigator Susana Chung in 2003.

// RsvpDialog.cpp// University of Houston College of Optometry// Copyright (c) 2003// PI: Susana Chung// Author: Hope M. Queener// // Main dialog for the rapid-serial-visual-presentation // psychophysical experiment control program

1.3.2 Include copyright information

Include copyright information, if appropriate, as comments at the beginning of the source code file. If code has any potential to be shared with other institutions, include this copyright statement:

% The copyright to the source code herein is the property of% The University of Houston College of Optometry, U.S.A.% This software may be used and/or copied only with the written% permission of the authors or in accordance with the terms % and conditions stipulated in the agreement or contract % under which the source modules have been supplied. % This copyright notice must not be removed.

1.3.3 When declaring variables, add comments on the same line

If a variable name has any ambiguity, add a comment to clarify its usage at its first appearance in the function or class. Typically, these comments fit on the same line as the variable declaration, and should be placed on the same line for compactness and clarity, as shown below. If a variable has an expected range of values, document the expected range. Variables that are grouped may be preceded by a comment that applies to the group.

20

Coding Conventions and Best Practices (Sept. 2009)

// Patient information at start of studyclass patientData{

int patientAge; // age in years expected to be 18 to 65double patientCylinder; // habitual refraction cylinder, dioptersdouble patientSphere; // habitual refraction sphere in dioptersdouble patientAxis; // habitual refraction axis in degrees

}

1.3.4 Comment on the purpose of a conditional block of code or loop

For conditional (if or switch/case) statements, provide the reason for the decision and a summary of the outcome. If loop statements, indicate the purpose of the loop.

// Connect to the camera and verify that it initialized properlyDcamCamera dcamCamera;bool cameraResult = dcamCamera.Open(“dcamConfig.txt”);if (!cameraResult){

MessageBox(“Unable to initialize camera”,”Warning”,MB_OK);return false;

}

1.3.5 Describe each routine briefly at the top of the routine.

The name of the routine will give a coarse description of the purpose, but a more complete description requires a sentence or two. If the routine can't be described briefly, consider if the routine might be broken up or more clearly organized. Ideally, the purpose of each argument is described for each routine, but if the argument names are obvious, these comments may be unnecessary. The habit of documenting all arguments is facilitated by the C# programming environment.

1.3.6 Document the source of algorithms that are used

If using an algorithm from a book or a journal, include a comment that references the source material. If the algorithm is original, describe any reference material that led to the design. If an algorithm is taken from a collaborating laboratory, include the contributors names from the other institution.

void ComputeFFT(double *signalAmplitude, long signalLength, double *signalFFT){/* The following code is an implementation of the Fast Fourier Transform algorithm explained in Digital Signals by Dr. Anonymous Scientist pg. 206. The imaginary component of the result is treated as negligible and discarded.*/// code for FFT on the input signal...

21

Coding Conventions and Best Practices (Sept. 2009)

}

1.3.7 Comment on the routine's limitations

If the routine provides a numeric result, document the accuracy of the result. For example, the comment explains that the imaginary component of the FFT is discarded in the previous example.

1.3.8 Document flags to the bit level

If a variable is used to isolate a bit, such as a control port for a digital input-output board, create hexadecimal constants for each bit that is meaningful to the application.

static int RESPONSE_LEVER = 0x0001; // bit 0 represents state of leverstatic int TONE = 0x0002; //bit 1 indicates if tone is on or offstatic int TRIGGER = 0x0004; // bit 2 indicates if trigger is on or off

22

Coding Conventions and Best Practices (Sept. 2009)

This page is intentionally blank

23

Coding Conventions and Best Practices (Sept. 2009)

1.4 General Error Checking and Debugging

1.4.1 Check the arguments to a function or procedure

Any routine should verify that all the arguments passed to the routine are valid in their type, value and range. An invalid argument that would disrupt the behavior of the routine should be caught prior to its use. The routine should use appropriate default values or return, as deemed appropriate.

1.4.2 Check preconditions and postconditions

Other conditions, aside from input arguments, may disrupt the proper execution of a routine. Check preconditions, such as file existence, initialization of pointer variables (C/C++), and existence of equipment that is expected to be used. Postconditions are conditions which need to be checked on exit from the member function. These may include that a file closed properly or a resource was otherwise released. Confirm that all return paths produce a meaningful result for the caller.

1.4.3 Use messages to signal code that should never be executed

Check for conditions that are not logically intended and return a message so that the developer can rectify the program to avoid such conditions. This technique is especially helpful for checking states in a switch-case block or nested if-statement where new cases may be introduced. The error messages will flag if the developer forgets to process the new cases in other parts of the project.

1.4.4 Detect and handle an error that affects the rest of a routine

For some functions, nothing can proceed if initial conditions are not met. For example, if an object was never created, then subsequent access to the object will cause an exception. The routine can instead detect that the object is uninitialized and return to the caller with an error state. Likewise, if a file is not opened properly for writing, subsequent write operations will fail.

1.4.5 If an error is detected in a routine, consider notifying the caller

If the error has a potential to affect your caller, it is important that the caller be notified. For example, the "Open" methods of a file class should return error conditions. Even if the class stays in a valid state and other calls to the class will be handled properly, the caller might be interested in doing some error handling of his own.

1.4.6 Remember that error handling code is vulnerable to errors

24

Coding Conventions and Best Practices (Sept. 2009)

Error handling code can also be defective. It is important to write a test case that will exercise that code.

1.4.7 Avoid using a returned argument for both error and result

Avoid using a single argument or return value for both the result and the error status. For instance, the “atof” c library function returns a floating point value from a character array. If it cannot convert the string to a number, it returns zero, which could also be a valid conversion from a string, so the result is ambiguous. It is better to have a function return results in a separate argument, and return a boolean or numeric status code to indicate success or failure. If a boolean result is returned, and the function could fail for several detectable reasons, it is advisable to provide either a “GetLastError” function to describe the error or a separate argument that gives more detail about the error.

1.4.8 Remove the error checking code that hinders performance

In C++ or C#, while developing the debug versions of code, include any error checking code that has the potential to illuminate problems. However, before producing the release version, remove or enclose in compiler flags any debugging code that can hinder performance. For example, a log file may be maintained during the debug process to verify all steps of an operation, but file access can be slow.

1.4.9 Retain error checking that can assist the user

As a general rule, error checking code that confirms proper initialization of a device, file or process is best retained. Any problems with the initialization should be described in meaningful pop-up message boxes, or referred to a log file so that the user can remedy simple problems such as a file that is already open or a device that is not connected.Code that performs validation on the arguments to a routine (and helps the function respond gracefully in response to bad arguments) should be left in, unless the checking is frequently called in a performance critical loop. To improve performance, arguments that are highly unlikely to be wrong (i.e. not dependent or user input, but on proper coding) can be checked by code that is only executed during the development phase and commented out or enclosed in compiler flags for the debug version.

1.4.10Use meaningful, polite and grammatically correct messages

Assure that messages to the user are respectful, meaningful and grammatically correct. In the rush of a deadline, developers may include messages that have misspelled words or vague errors messages that refer to variable names. Remember that the messages you display for debugging and tracing might show up in the release version of the code and aim to describe the problem to the typical user. Avoid messages that are joking or rude. Even if they are never intended to appear to the end user, they may be overlooked at release time.

25

Coding Conventions and Best Practices (Sept. 2009)

1.4.11Reserve assertions for debugging purposes.

In C++ or C#, avoid using assertions to check error codes of a function. If a function can fail, handle the error with error handling code, rather than an assertion. Assertions are compiled-out of production code, therefore reserve them for debugging purposes only.

1.4.12Test subsystems with parallel algorithms in debug versions

Consider the situation where a slow algorithm needs to be optimized. The slower original can be retained in a separate module (or if in C++ kept in the source code and enclosed by compiler flags). Then, a switch (flag) variable can be used to select the slower, trusted code or the untested, optimized code, and the results compared. Such a method allows the developer to compare the results of either algorithm to the same input data very quickly.

26

Coding Conventions and Best Practices (Sept. 2009)

This page is intentionally blank

27

Coding Conventions and Best Practices (Sept. 2009)

2 MATLAB Coding Conventions

2.1 Source Code Organization

2.1.1 Use functions rather than scripts

Always wrap MATLAB code in a function rather than writing scripts. Scripts load all defined variables in the global MATLAB workspace. As in all languages, global variables promote poorly managed code. One specific example is when a project grows to more than one m file and the same variable is unintentionally used in both scripts for different purposes. The use of functions keeps variables local and requires the developer to pass the necessary information into the function and return the necessary result intentionally.

2.1.2 Use separate m-files for generic operations

When a function may be used by more than one m file, it needs to be in a separate m file so that both calling functions can find it in the MATLAB path.

2.1.3 Keep specific functions in the same m-file that calls them

If your application calls a function that is unique to your application, and unlikely to be of generic use, place the function in the same m-file. This practice of declaring a “subfunction” helps to minimize the number of m-files associated with a project. Since MATLAB code is not typically compiled into a single executable file, keeping the functions in a few modules makes it easier to share the code. Some legacy MATLAB code has many functions because early versions of MATLAB did not have a way to access internal functions as callbacks from the uicontrol (user interface) functions. The “@” operator has resolved this limitation. Do keep in mind, however, that if files become very long, or if particular functions need to be shared between several modules, it makes sense to break routines up into separate files according to functionality.

2.1.4 Add only general toolboxes to the MATLAB path

There are two approaches to making your source code available to MATLAB, adding your folder to the MATLAB path, or changing directories to your folder to make it the current path. In general, it is best to keep your own work in your own folder. If more than one user runs MATLAB on the same machine, adding to the MATLAB path the working folders from all users or all projects gets very messy. Instead, change to your working folder and let only the MATLAB and toolbox paths be in the MATLAB path.

28

Coding Conventions and Best Practices (Sept. 2009)

2.2 Naming Conventions

MATLAB uses an all-lowercase naming convention for its function names and examples. By following the general naming conventions described in section 1 of this document, it will be generally easy to distinguish your code from MATLAB inbuilt functions because your code will use longer, mixed-case, descriptive names. Other situations unique to MATLAB are specified in this section.

2.2.1 When using GUIDE, rename the tag property of each control

When you use GUIDE to create a control, such as a pushbutton or edit control, the default tag name is the style of control appended by a number, such as “edit1”. Always edit the tag property so that the information maintained by the control is clear from the control’s tag string. Retain the style name as the first part of the tag name, but append the name with a descriptor instead of the number. For instance “editSubjectName” or “pushbuttonOk”.

2.2.2 When using GUIDE, retain the automatic function names

When MATLAB generates the code for a GUIDE generated user interface, it uses a systematic naming convention for the default functions, based on the tag name, and followed by “_Callback” or “_CreateFcn”. These function names do not follow the convention described in section 1, but because MATLAB consistently generates the names in this manner, it is better to leave them in their original state. If the control tags are sufficiently descriptive, these function names will be easy to understand, and the unique MATLAB naming convention serves as a reminder that these are special, automatically generated functions.

2.2.3 When using GUIDE, use the handles structure carefully

When you use GUIDE to create a graphic user interface (GUI) for MATLAB, a special structure, called “handles” is associated with the user interface. This structure initially contains one field for each control, and each of those fields has properties that describe the control. The program can add fields to the handles structure, such as the results of callback functions. Proper maintenance of this structure is essential to the function of the GUI code. Access the handles structure using the guidata function. If you modify the handles structure, you must also call the guidata function again to update the application’s internal storage of the handles structure. This technique is somewhat like maintaining a global variable, but the access to the data is restricted to the gui by use of the guidata function. When passing the handles structure as an argument to callback functions or your own functions, keep in mind that the internal handles structure is only updated by the guidata function. Consider using the name ‘tempHandles” when using a temporary copy of the handles structure, if the copy will not be later used to update the internal handles structure.

29

Coding Conventions and Best Practices (Sept. 2009)

2.3 Programming Conventions

Refer to the MATLAB help topics “Using Memory Efficiently” and “Techniques for Improving Performance” for descriptions of how to best implement MATLAB code.

2.3.1 Remember that MATLAB was developed to do math

It is good to remember that the fundamental design of MATLAB was for solving vector and matrix problems. Its history is as a tool for mathematics. Vectors are efficient. For-loops are less efficient. The variable “i” is predefined as the square root of -1. The multiplication operators to use depend on whether the intent is a matrix multiplication or an element-by-element multiplication. Staying aware of the mathematical purposes of MATLAB can help the developer understand when MATLAB behaves differently than would similar code in C or Java.

2.3.2 Remember that for-loops in MATLAB differ than for-loops in C

In C, C++, C#, a for-loop continues until the conditions of the loop are not longer met. The syntax in C, etc. is “for (row=0; row<10; row++)” meaning that if i becomes greater than or equal to 10, the loop stops. If inside the loop the statement “row=11” is made, the loop stops. In MATLAB, the operation is rather a progress through a list. The statement “for row=1:10” is actually, “for each item in the list [1:10]”. If row is reassigned in the middle of the for-loop, the loop continues for the remainder of the list. A “break” statement, however, will end the loop early, in either language.

2.3.3 Use specific path names to files rather than writing to the same folder as the source code or using the ‘cd’ command

When opening or saving files during a MATLAB program specify the path to the file explicitly in the file name variable of the function. For example, the first argument to the ‘xlsread’ command is the file name. In the example code in the MATLAB help, no path information is given, only the name of the file. However, in practice, the user is likely to want to specify where the files are located. The functions uiputfile and uigetfile can be used to let the user specify the folder and file name that are desired. The function fullfile can be used to concatenate the path and file names regardless of the operating system. While MATLAB allows the use of the ‘cd’ command to change directories, doing so is not recommended because the source code files may not be properly found.

2.4 Source Code Documentation

No unique MATLAB conventions are currently necessary.

30

Coding Conventions and Best Practices (Sept. 2009)

2.5 Error Checking and Debugging

When developing MATLAB programs, it is important to include graceful error handling. When MATLAB encounters an unhandled error, it generally reports the error and all subsequent execution stops. The error reported will point to the line of code that failed, which a general user has not power to address. Good MATLAB code handles common errors such as a file not being found or a typing mistake by reporting the error and giving the user the opportunity to resolve the problem on their own.

31

Coding Conventions and Best Practices (Sept. 2009)

This page is intentionally blank

32

Coding Conventions and Best Practices (Sept. 2009)

3 C++/MFC Coding ConventionsMost of the C++ code produced by the UHCO Core Programming Module between the years 2000 to 2007 was developed using Visual Studio version 6.0 with Microsoft Foundation Classes (MFC) providing the based classes for dialog-based applications. Much of this legacy code retains older conventions promoted by that generation of Microsoft products, such as the Hungarian notation typical of the MFC example code. New projects should be developed using managed C++ under Visual Studio .net 2005 and adhere to the style suggested in Section 1 of this document.

3.1 Source Code Organization

3.1.1 Match the name of the class to the name of the source files

When creating a new generic class, use the name of the class as the name of the header file (.h) and the implementation file (.cpp). Rarely, one will see a class file not named to match the class. One exception to this rule is if a source file contains a primary class and a smaller class used exclusively by the primary class. Or, if a header file contains many type classes used in a project, that file could use a generic name such as “commonTypes.h”. Note that in Visual C++ 6.0, class names have a leading “C” compared to the file name. Since these classes are based on existing MFC classes, retain the leading “C” to distinguish them from generic classes. In general, the leading “C” is not necessary since all modules in the C++ project are typically classes.

3.1.2 Name the header files with care so that they don’t conflict

Special care should be given to the naming of header files because of potential conflicts between modules or with other libraries.

math.h // avoid (this is a C math library header file)ImageAnalysis.h // recommendedSignalAnalysis.h // recommended

3.1.3 Use appropriate extensions for source files

The development environment typically handles this one for the developer.

Extension Description.C C Source Files (rarely used with C++ files).CPP C++ Source files.H C/C++ Header files.INL C++ Inline function files (rarely used).IDL (.ODL) Interface Description language.RC Resource Script

33

Coding Conventions and Best Practices (Sept. 2009)

3.1.4 Avoid hard-coded path names in source files

Use only the file name, not the absolute path in header file include statements. Use the devlopment environment tools to set up the special include paths (and library paths) in the project settings. If a file is used from another folder within your project organization, use relative path (“..\”) notation rather than abosolute paths. This is especially needed when projects are moved to another computer or to another user’s folder on the same computer.

// Use project settings to make third party libs visible to project#include "..\common\tifftools.h;" // recommended#include "DcamSdk.h" //recommended; #include "C:\Program Files\DCAMSDK\Include\DcamSdk.h" // avoid

3.1.5 Include only those header files which are required

#include <iostream.h> // required#include <math.h> // not required, should be deleted

void main(){

cout<< “Hello World”;}

3.1.6 Declare functions and variables only in header files

Some legacy C code or C++ coded in the style of C may declare function prototypes or external variables at the top of the implementation file. Avoid this outdated practice.

3.1.7 Use the MFC Version Resource

When using MFC, use the version resource (VS_VERSION_INFO) to indicate the version of the code released to users (i.e. installed on machines used by lab members). Update the version number with each release. Maintain a history of code revisions either in a readme.txt file, in the application source file (if it is dialog based and the main application source file is short), or in a separate document. Keep the history file in the project folder.

3.1.8 Build debug and release executables to their respective subdirectories

Visual Studio (C# or C++) provides a way to designate the destination folder for the executable files in the project settings menu. As a general rule, build the release version to the default subfolders set up when you created the project in Visual Studio (i.e “\Release” and “\Debug”). Maintain the intermediate files in these folders as “disposable” (except for the release executable). Do not put files that need to be permanently stored in these folders.

34

Coding Conventions and Best Practices (Sept. 2009)

3.2 Naming Conventions

Along with the general best practices set forth in section 1, these additional naming conventions can enhance C++ source code.

3.2.1 Specify member variables of a class as “private” and follow camel notation

By specifying member variables as private (or protected) the class protects its data by screening input and verifying output with set and get property functions. This convention forces the developer to make sharing of data outside the class a deliberate choice. However, sometimes, the primary purpose of a class is to encapsulate a set of parameters. In this case, making all the members public is appropriate.

3.2.2 Consider a special prefix for member variables

When reading through a class, it can be helpful to distinguish member variables from local variables. The Visual Studio 6.0 generation of code used “m_” as a prefix to designate member variables. The developer has the option to maintain this convention, and in this case, member variables are an exception to the “no underscores” rule. Alternately, the developer could simply prefix all member variables with “m” and capitalize the rest of the variable names. If the developer chooses a member-variable naming convention, it should be followed for all modules in the project.

Legacy styleclass RsvpStimulus {

private:double m_letterSizeDegrees;// lowercase x heightdouble m_letterContrast; // Lmax-Lmin/Lmax+Lmin

...}

Alternate styleclass RsvpStimulus {

private:double mLetterSizeDegrees;// lowercase x heightdouble mLetterContrast; // Lmax-Lmin/Lmax+Lmin

...}

3.2.3 Name static member variables using all caps

Where possible, use static constant member variables rather than #define constants. When doing so, use whatever member variable naming convention (as described above) and use all capital letters to emphasize that the value is constant.

35

Coding Conventions and Best Practices (Sept. 2009)

3.2.4 Prefix pointer variables with lowercase “p”

This optional practice may be helpful in unmanaged (native) C, where attention to pointer variables is especially important. For member variables, prefix pointers with “mp” or “m_p”. For local variables, prefix with “p”. This distinct designation on pointer variables helps to remind the developer that pointers must be initialized and deleted with care. Always initialize pointer variables to NULL.

double *m_pZernikeCoefficients; // recommended for member variablem_pZernikeCoefficients=NULL; // recommended in constructordouble *pZernikeCoeffiicents=NULL; // recommended for local variable

3.2.5 Use get and set property methods to access member variables

Most custom classes need to expose only a subset of the member variable content. In this case, expose private member variables only by get or set property functions, which screen input and verify output. Use Pascal case, as with any method or function. This convention forces the developer to make sharing of data outside the class a deliberate choice. The naming convention clarifies that the function is simply setting or getting data, with screening or validation operations, but not performing additional calculations. This also allows the set and get property functions to be used much like variables in that they only return or accept data of the same type as the member variable. An exception to this rule is for classes, such as forms or dialogs, that have the primary purpose of exposing member information to other classes.

class RsvpStimulus {private:

double m_letterSizeDegrees;// lowercase x heightdouble m_letterContrast; // Lmax-Lmin/Lmax+Lmin

public:void LetterContrast(double letterContrast);

double LetterContrast();void LetterSizeDegrees(double letterSizeDegrees);double LetterSizeDegrees();

}

3.3 Programming Conventions

3.3.1 Use static member variables or constants rather than hard-coding numerical values

Numerical constants like the maximum size of a data structure or mathematical constants such as (pi) should be declared as static member variables or constants. Static member variables are recommended over constants. For values that are known at compile time, the value can be hard-coded in the constructor. For values that require programmatic input, but are constant after initialization, the initial value may be passed to the constructor and the static scope protects the value of the variable. The use of #define for

36

Coding Conventions and Best Practices (Sept. 2009)

constants is present in much legacy code, but because such a method is effectively global in scope, it is discouraged. If static variables are needed externally, rather than declaring them public, create a get function for retrieving the value.

3.3.2 Terminate Case Blocks

Statements following a case label should be terminated by a statement that exits the switch statement, such as “return” or “break”. The complier does not generate warnings for this condition, so it is an easy error to overlook. The developer must conscientiously examine each switch-case logic statement. Leaving out such termination produces a fall-through between different cases, which is rarely intentional. In rare cases where fall-through might be necessary document the intention of fall through.

int rsvpResponse = RsvpDialog.DoModal();switch (rsvpResponse){case IDOK:

MessageBox(“Thank you for your time today.”);break;

case IDCANCEL:MessageBox(“Please come back another time.”)break;

default:MessageBox(“Unrecognized result from RSVP”);return;

}

3.3.3 Declare variables wherever they are used.

In C, variables are declared at the beginning of a function but in C++ they may be declared just prior to use. By declaring variables nearest to the first use, the type of the variable is clear and the lifetime of the variable is limited to its useful duration. Generally, it is good practice to insert a blank line just before the new variable declaration and the block of code that uses it.

void RsvpStimulus::PrepareStimulus(){

prepareColorLookupTable();prepareFixationPage();prepareAdaptationPage();

int stimulusLevel = int(rand()*5); // select stimulus levelprepareStimulus(stimulusLevel));

}

3.3.4 Initialize variables wherever they are declared

37

Coding Conventions and Best Practices (Sept. 2009)

Initialized declarations avoid redundant function calls. Two function calls are needed if an object of a class is assigned without using the default constructor. Always initialize pointers to NULL. For member variable pointers, set them to NULL in the constructor and reset them to NULL after deleting them. Check member pointers for NULL before using them in class functions.

int zernikeModes=65;double *zernikeCoefficients = NULL;zernikeCoefficients = new double[m_zernikeModes];...delete [] zernikeCoefficients;

3.3.5 Place new and delete statements so as to emphasize their relationship with each other

In unmanaged C++, specifically, use new and delete at 1) the beginning and end of a routine or 2) at the beginning and end of immediate use or 3) in the constructor or initialize of a class and in the destructor. If the allocated data must be used throughout a routine, use new at the beginning of the routine and delete near the end. If the allocated data is used for a compact block of code use new and delete immediately before and after the data is used, respectively. It is important to be aware that the compiler does not complain if the code omits the [] in the delete statement for an array, but doing so does not properly delete the array and can lead to memory leaks.

// an example new/delete for a member variableclass RsvpExperiment // class declaration{...

private int *m_pStimulusList;...}

void RsvpExperiment::RsvpExperiment(int stimulusLevels) // constructor{...

m_pStimulusList = new int[stimulusLevels];...}

void RsvpExperiment::~RsvpExperiment() // destructor{

if (pStimulusList != NULL) delete [] m_pStimulusList;}

// an example new/delete pair for a localized variablesvoid RsvpExperiment::CreatePages{

CString *pWordList = NULL;SelectSentence(); // selects index into stored sentencesint wordCount=SentenceLength(); // gets length of current sentencepWordList = new CString[wordCount];CopySentence(pWordList,wordCount);//copies current sentence

38

Coding Conventions and Best Practices (Sept. 2009)

for (int wordIndex=0; wordIndex<wordcount; wordIndex++){

DrawWord(pWordList[wordIndex]);}// delete pWordList; // this syntax will compile, but is wrong!delete []pWordList;

}

3.3.6 Be vigilant about not accessing the pointer to an object which has been deleted

In unmanaged C++, accessing a pointer to a deleted object causes an undefined behavior and would crash the program. Once the object is deleted the pointer to it can be assigned to NULL or a new valid object. If an object pointer may be invalid, check it before using it. This may particularly happen if the code to access the object and code to delete the object are in different threads. Since the priority of threads can change anytime through switching, it is possible that the object will be accessed after it was deleted.

// Allocate the array for a camera capture buffervoid classname::InitializeImageBuffers(){

ReleaseImageBuffers(); // free memory if it was in usem_imageWidth=DcamCamera.ImageWidth();m_imageHeight=DcamCamera.ImageHeight();m_pCapturedImage = new char[m_imageWidth*m_imageHeight];

}

// Confirm capture buffer is null before exiting or reallocatingvoid classname::ReleaseImageBuffers(){

if (m_pCapturedImage) // set to NULL in constructor{

delete [] m_pCapturedImage;m_pCapturedImage = NULL;

}}

// Confirm capture buffer is not null before copying data into itvoid classname::RetrieveImageFromCamera(){

if (pCapturedImage) // != NULL){

DcamCamera.CopyImage(pCapturedImage);}

}

3.3.7 Avoid the use of macros unless they are absolutely necessary

Macros are very hard to debug because the compiler doesn't generate the proper symbols for them. Unless they are necessary for conditional compilation or a similar purpose, they should not be used and should be replaced by inline routines.

39

Coding Conventions and Best Practices (Sept. 2009)

3.3.8 When nesting conditions and loops, use curly braces to clarify the statements belonging to each condition

Although C/C++ allows single line conditions to be written without curly braces, if a statement is nested among loops, it is highly recommended that curly braces be used for all conditions of the statement. If both the “if” and the “else” condition have single-line results with parallel form, curly braces can generally be omitted without sacrificing clarity. If the “if” block is multiple lines and the “else” block is one line, use curly braces on both to keep a parallel form.

// avoid this form: end of first if statement is difficult to spotif (stimulusReady) if (fixationEnabled)

{ ShowFixation(); ShowStimulus(); ShowFixation();

}else

ShowStimulus();

// recommend this form; blocks are clearly shown in curly bracesif (stimulusReady){

if (fixationEnabled){

ShowFixation(); ShowStimulus(); ShowFixation();

}else{

ShowStimulus();}

}

// acceptable to have parallel statements without curly bracesif (leftAlternative)

ShowLeftStimulus ();else

ShowRightStimulus();

3.3.9 Conditionals in the “if” statement should be free of assignment operators.

Sometimes one will see an example where a conditional statement depends on the result of a function call. If the result is used for any purpose after the conditional, the result should be assigned before basing any conditions on it, for clarity and ease of maintenance. If the result is not used for any purpose other than deciding what branch to take, no assignment is needed.

40

Coding Conventions and Best Practices (Sept. 2009)

// no assignment is neededint temporalInterval;if (rand() < 0.5)

temporalInterval=firstInterval;else

temporalInterval=secondInterval;

// assignment should take place before the conditional statementint temporalInterval;int stimulusLevel;intervalChance = rand();if (intervalChance < 0.5){

temporalInterval=firstInterval;stimulusLevel = (int) intervalChance*5;

}else{

temporalInterval = secondInterval;stimulusLevel = (int)(intervalChance – 0.5)*5;

}

3.4 Source Code Documentation

This section to be expanded at a later time.

3.5 Error Checking and Debugging

3.5.1 Throw an exception only when required

In the case of exceptional conditions, it is sometimes useful to simply throw an exception. If using the throw statement, exercise the same caution as when using the return statement. Throw statements constitute an exit point of a routine and one must assure that all the resources that the routine uses are cleaned-up automatically. Fortunately, objects declared on the stack will be cleaned-up properly.

In addition, routines that throw exceptions must mention it in their documentation. If the caller of routines can throw exceptions, it is important that the caller prepare for that eventuality. The caller can decide to catch the exception and handle it or decide to let it pass through. If the exception is passed, the caller must behave as if it were throwing the exception itself and mention it in the custom documentation.

Never forget that exceptions should be used for exceptional conditions. They shouldn't be used for "casual" flow control because they are slow. Pointers to objects declared on the stack will not be freed automatically when an exception is thrown.

41

Coding Conventions and Best Practices (Sept. 2009)

3.5.2 A class should be left in a defined state in case of failure

The class can be put in a state where it can't do much. For example, member functions that depend on the file being open should check whether the file is open and then proceed. If there is a failure and the file is not open it should return an appropriate error message.

42

Coding Conventions and Best Practices (Sept. 2009)

4 C# Coding ConventionsThis section to be expanded at a later time.

4.1 Source Code Organization

4.2 Naming Conventions

4.3 Programming Conventions

4.4 Source Documentation

4.5 Error Checking and Debugging

43

Coding Conventions and Best Practices (Sept. 2009)

This page is intentionally blank

44

Coding Conventions and Best Practices (Sept. 2009)

5 Visual Basic Coding ConventionsThis section to be expanded at a later time.

5.1 Source Code Organization

5.2 Naming Conventions

5.3 Programming Conventions

5.4 Source Documentation

5.5 Error Checking and Debugging

45

Coding Conventions and Best Practices (Sept. 2009)

This page is intentionally blank

46

Coding Conventions and Best Practices (Sept. 2009)

6 Java Coding ConventionsThis section to be expanded at a later time.

6.1 Source Code Organization

6.2 Naming Conventions

6.3 Programming Conventions

6.4 Source Documentation

6.5 Error Checking and Debugging

47

MFC Development Guidelines

References

MATLABMATLAB Programming Style GuidelinesDatatool20035 SE 27th Place, Sammamish, WA 98075; Ph: 866-633-0432http://www.datatool.com/downloads/matlab_style_guidelines.pdf

C++Francis Beaudet, Fred Boulanger, Stephane Lussier, Claude Monpetit, Coding Conventions for C++ and Java Applications, Macadamian Technologies Inc., Ottawa, Ontario.http://www.macadamian.com/codingconventions.htm

Mats Henricson, Erik Nyquist and Ellemtel Utvecklings, Industrial Strength C++, Prentice Hall (out of print), 1997. http://hem.passagen.se/erinyq/industrial/

.NET Framework General ReferenceDesign Guidelines for Class Library Developershttp://msdn2.microsoft.com/en-us/library/ms229042.aspx

Visual BasicAvailable from the Microsoft Developer Network Library.Visual Basic Coding Conventionshttp://msdn2.microsoft.com/en-us/library/h63fsef3(VS.80).aspxVisual Basic Naming Conventionshttp://msdn2.microsoft.com/en-us/library/0b283bse(VS.80).aspx

JavaCode Conventions for the Java Programming Language, Sun Microsystemshttp://java.sun.com/docs/codeconv/