|
I. CAPITALIZATION CONVENTIONS This section suggests guidelines for capitalization in IDL coding documentation.
| Table 1: All Upper Case (Capitalization) Usage | | Items | Examples | | IDL routine declarations | PRO; FUNCTION; END | | Keywords | FORMAT=myformat isurface, data, /OVERPLOT | | Type identifiers | 0L; 1.2D; 34ULL | | COMMON block definitions | COMMON MYGLOBALS, doneFlag, shareFile | | Jump statements and labels | ON_IOERROR, JUMP1 if a ne 1 then GOTO, JUMP2 | | IDL compiler reserved words | COMPILE_OPT; FORWARD_FUNCTION |
| Table 2: All Lower Case Usage | | Items | Examples | | IDL reserved words not named above | case; if; eq | | IDL routine names | print, 'Hello' result = dialog_message('Hello') | | Output file names | save, data, FILENAME='my_output_file.sav' | | Most short variable names | x; winid; datafile |
| Table 3: Mixed Case Usage | | Items | Examples | | Long variable names | myWindowHeight = 200 PRO foo, MYLONGPARAM=myLongParam | | Var names with type prefix | oView; pState; wButton | | Class names | PRO MyClass::Run oMC = obj_new('MyClass') | | Class/var names with all-cap acronyms | IDLgrWindow; oXMLDOMNode; myHDFfilepath | | Class Method Names | PRO IDLitWindow::OnMouseMove oWin->Draw |
II. NAMING CONVENTIONS The most important guideline for naming in code examples is simple: be meaningful. That is, use clear, descriptive names, avoiding short, abstract ones. For variables, for example, for a meaningful name use height instead of h.
A. Variable Naming Conventions This standard advocates using the following prefixes to help identify certain kinds of common programming objects:
| Table 4: Type Prefixes for User-Defined Variables | | Type | Prefix | Example | | Vars that hold counts | n | nSamples; nLines | | Object reference | o | oWin; oROI | | Pointer | p | pData | | Widget ID | w | wLabel2; wBtnExit | | Compound Widget ID | cw | cwMenu; cwUserPrompt | | iTools Object identifiers | id | idTool = itgetcurrent(TOOL=oTool) |
Note that in the case of widget ID's, this standard recommends that you not just prefix with the 'w' character, but, if there are lots of same-type widgets, use a prefix designating the widget type, e.g. wTextTemp/wTextPressure, wBtnSubmit/wBtnCancel.
Further, combine prefixes when relevant. E.g., if you assign a pointer to wList, for full clarity name your new pointer variable: pwList.
Because of these prefixes, you don't need to use redundant descriptive strings such as "Pointer" or "Widget". Just use pData, not pDataPointer; just use wBaseHistInfo, not wHistInfoBaseWidget.
Finally, when there is ambiguity, use the suffix "s" or "Arr" in the names you assign to array variables, as in:
mystruct = { radius:0D, location:intarr(2) } mystructs = replicate(mystruct, 100) ; ... alternatively, myStructArr = replicate(mystruct, 100)
B. Routine Naming Conventions First of all, we cannot emphasize enough that the names of user-defined routines (or user-defined classes) should be unique. If you create too generic a name, e.g. "plot_hdf", you have no way of knowing that this will not in later years collide with a routine of same name in a future IDL or a future third-party library that you download. Go with the minimum intuitive name that you believe will always be unique in your IDL community's environment, e.g. "plot_noaa_coral_reef_hdf".
To distinguish long routine names from long variable names this standard advocates that all IDL procedures and functions have all lower-case names. To make these long names more readable, use underscores in routine names where you might otherwise use capital letters in variable names. For example:
; assignment from a variable data = myComplicatedVarName[10, 20, 2] ; assignment from a function data = my_complicated_function_name(10, 20, 2) This standard proposes a very different syntax for IDL Class method names. We recommend that these follow the standard you see in the class source code in IDL's 'lib' directory: Capitalize the first letter of all the main substrings in an IDL class method name. E.g.
PRO MyTextDisplayer::GoToTheBottomOfThePage ; handles calls like oTextDisplayer = obj_new('MyTextDisplayer') oTextDisplayer->GoToTheBottomOfThePage This standard has two prefix rules for naming of routines. These follow consistent IDL conventions you would see in IDL's 'lib' source code directory.
- For compound widget programs, use the prefix "cw_" in your routine name, e.g. "PRO cw_multilist".
- For non-method routines associated with a particular class, prefix the names with ClassName__ (two underscores) to provide both symmetry and uniqueness:
; Naming a method for MyClass PRO MyClass::PromptUser ; Naming a helper (or "friend") routine for MyClass PRO MyClass__HandleEvents, event
C. KEYWORD Naming Conventions As mentioned in Table 1 of this Tech Tip, keyword names should have all upper-case lettering. Beyond this, use full keyword names, not abbreviations, for clarity and because future keyword names might make current abbreviations ambiguous. For example:
message, msg, /INFORMATIONAL ; not /INFO file = filepath('img.jpg', SUBDIRECTORY=['data']) ; not SUB=... Keep in mind that while the keyword name should be all caps, the variable assigned to it should follow the variable naming conventions shown in sections I and II A above, i.e. all-lower-case or mixed lower-and-upper with a lower-case start, e.g.:
myMinValue = min(data, MAX=myMaxValue)
III. SPACING AND INDENTING This section suggests guidelines for spacing and indenting in IDL coding documentation.
A. Line Widths This standard recommends that whenever an individual IDL call exceeds 80 characters in width, you break that call up into a multi-line call using the IDL '$' continuation operator. This is the best way to insure that your code will be reader-friendly in most every text editor/viewer that your IDL community uses.
B. Spacing In general, do not put spaces before or after parentheses or brackets. Examples:
nLines = file_lines('mydata.txt') ; not nLines = file_lines( 'mydata.txt' ) textArr = strarr(nLines) ; not textArr = strarr (nLines) mytile = myimage[*, *, 64:128] ; not mytile = myimage [ *, *, 64:128 ] However, if the parentheses are being used to make a long expression more readable, then put a space on either side of the expression. Example:
if ( height gt thresh and (thresh ne 0 or height ne 100) ) then begin You might even add spaces before and after a parenthesis to distinguish a superexpression from its subexpressions, as we show above. Separate parameters in procedure and function calls by a single space after commas:
myprocedure, arg1, arg2, arg3 output = myfunction(arg1, arg2, arg3) Put a space on either side of operators except for the -> symbol(the class method-invocation operator). Omit the spaces, however, when the expression is to be taken as a single unit, such as in for statements and in keyword assignments. Examples:
; Spaces around the operator nPixels = nSamples * nLines ; No spaces around the method-invocation operator, -> oModel->Add, oAxis ; No spaces around the operator in a for loop (nSamples*nLines-1 is ; treated as one unit) for i = 0, nSamples*nLines-1 do begin ; No spaces around the operator (nColors*stepSize is one unit) output = bytscl(input, TOP=nColors*stepSize) Note: You could also use parentheses around (nSamples*nLines-1) or (nColors*stepSize) to make the code that much clearer to readers. Parentheses are optional. With or without them, IDL still interprets the expression the same way.
This guide also recommends putting spaces on each side of the '=' operator except in one case: Omit the space buffering in keyword assignments. Notice the difference in spacing between the TOP=... assignment above and the output = ... in the last example above.
C. Indenting Indent all continued lines 4 spaces beyond the starting "column" of the first line of the continued statement. Make sure you have a hard return after every line-continuation character ($).
Left-justify routine delimiters (PRO/FUNCTION and END), file header statements and comments on routines, and the first-level statements within a PRO/END block. Lines contained within conditional blocks of code, such as within for or case statements, should be indented an additional four-space increment.. Indent further four-space increments for nested conditional statements. Comment lines should have the same level of indent as the code lines they describe. The following example illustrates these levels of indentation:
PRO IncrementSize ; Begin first code line at left margin if i lt 10 then begin ; Begin first nested conditional statement 4 spaces in if j lt 8 then begin ; Begin second nested conditional statement 8 spaces in for k = 0, 2 do begin ; Begin third nested conditional statement 12 spaces in ; Long TITLE exceeds 80 char linewidth, so we use line ; continuation char and indent next line 4 more spaces plot, mypressure[k], mytemp[k], $ TITLE='Pressure vs. Temperature in My Experiment' endfor ; Line up with corresponding 'for' statement endif ; Line up with corresponding 'if' statement, etc. endif END
Note: Using explicit end statements (e.g., endif instead of end) is considered a good practice. In if, for, or while blocks, if the conditional blocks consist of only one statement each, then either a) enter the whole statement on a single line (if the whole block is very short) or b) use '$' line-continuation characters rather than begin/end statements. Examples:
; Very simple and very short conditional block if strlen(file) eq 0 then print, 'Empty String' else print, file ; Very simple conditional block with longer statements if strlen(file) eq 0 then $ print, "The variable 'file' is curiously devoid of any value" $ else $ print, "Here is the name stored by variable 'file': ", file ; Notice the pretty lineup of "if" and "else" ; Likewise, a very simple for loop for i = 0, n_elements(myarr)-1 do myarr[i] += (randomu(seed) - 0.5D)
; In conditional blocks with multiline statements begin/end is indispensible if strlen(file) eq 0 then begin print, 'No file selected. Try again.' file = dialog_pickfile() endif while not eof(lun) do begin readf, lun, line text[lineIndex++] = line endwhile The rules for case and switch statements are similar to above - omit begin/end statements if the conditional expressions are simple. Regarding indenting, line up the case options with the starting column of the case block. Indent all code except the end statement within each option block. Any multiline block within a case option should be wrapped in begin/end statements; do not ever replace those with '$' line continuation characters. Example:
case event.id of info.wBtnExit: widget_control, event.top, /DESTROY info.wBtnOpen: begin file = dialog_pickfile() if strlen(file) ne 0 then begin process_this_file, file print, 'File has been processed.' endif end else: print, 'Control reached end of case statement' endcase Finally, this standard would remind you that code is most readable in fixed width font. This allows indentations to show their lineup, helping you to match beginning and end elements of code sub-blocks. Consider this when publishing to share with others.
IV. OTHER GOOD PRACTISES TO USE
A. Use square-brackets, not parentheses, for array subscripts IDL allows subscripting with parentheses delimiters, e.g. myFirstScanLine = image(0, *), but there are very few other modern programming languages that do so. To make your code more readable for programmers trained in other languages use square-brackets only - image[0, *] - in your IDL code.
B. Define strings with single-quotes, not double-quotes This makes your code consistent with the examples used in IDL's official documentation and in IDL's open source libraries. Little-known fact: Single-quotes characters inside a string can be "escaped" by doubling them up. Thus, this style of quoting:
print, 'To "reject" press the ''Don''t Stop'' button' can eliminate the need for the better-known, but klunkier, double-quote solution:
print, 'To' + ' "reject" ' + "press the 'Don't Stop' button" C. Clean up all your heap variable references Use PTR_FREE or OBJ_DESTROY to clean up your heap variable references before your program returns. Anything your program leaves leaked on the heap could affect your end-user's IDL session even after he/she is done with your program. Loook out particularly for these scenarios:
; Your widget program may have a state structure variable that ; is a pointer. In that case ... widget_control, event.top, GET_UVALUE=pState ptr_free, pState ; or the state struct may not be a pointer, but one of its ; fields is. In that case ... widget_control, event.top, GET_UVALUE=state ptr_free, state.pImageData and:
; In object graphics look out particularly for objects that are ; not part of an IDLgrWindow's GRAPHICS_TREE hierarchy obj_destroy, oWin ; This step is well-known ; ... but don't forget this: obj_destroy, [oPalette, oFont]
V. PRACTISES TO AVOID
A. Avoid use of the '&' IDL Command Concatenator operator Avoid putting more than one statement on the same line using the & character. As an example, it can be hard to find the redefinition of 'mystruct' in code that includes lines like this:
openr, lun, 'mydata.txt' & readf, lun, temp & mystruct.wl=temp B. Avoid using obsolete routines Besides the obvious technical danger of relying on routines that may no longer be compatible with latest generation O.S.'s, there is a stylistic danger in obsolete routines. Programmers reading your code, even you yourself, may at some point no longer have adequate documentation about the use or syntax of those routines.
C. Avoid having a single parameter be both an input and an output Code can be both hard to read, hard to debug and very hard to document when the variable that carries the input values to an IDL routine is modified in value before that routine returns. Define rather an additional parameter to carry the modified value.
D. Avoid modifying system variables Particularly if you are sharing your IDL programs with others, it would be considered invasive to modify other users' IDL environment configurations without their explicit approval. If your program does have to change users' environments, use whenever possible the built-in routines provided by IDL for modifying IDL environment variables. Finally, be considerate of your users by restoring their environment settings when you are finished. An example of good practise:
device, GET_DECOMPOSED=oldDC ; save the user's current setting device, DECOMPOSED=0 T3D, MATRIX=oldGraphicsTransformMatrix ; save user's !P.T setting T3D, newMatrix ; do this instead of !P.T = newMatrix ; [ your plot commands go here ] ; ... at the end restore the user's original environment T3D, oldGraphicsTransformMatrix ; do this instead of !P.T = oldGr... device, DECOMPOSED=oldDC E. Avoid coding new IDL System Variables Once again, particularly if you are sharing your IDL programs with others, it can be both invasive and a little dangerous to foist new IDL system variables onto other users' IDL environments. If you need to pass a setting around globally inside your program, why not use an IDL COMMON block instead?
The example below demonstrates all the principles described above. You can download a nicely printed version of this style guide, including the example, at this link: One Proposal For An IDL Coding Standard.
|