Programming Style

The purpose of this document is to collect in one place suggestions about programming style to be used in the Ptolemy project. This document is available for use at the University of Texas at Austin with permission of the Ptolemy project at the University of California at Berkeley.

SUMMARY OF SUGGESTIONS BELOW

  1. Think of your software as a publication.
  2. If you have to use global variables, use long descriptive names.
  3. Never guess about data structure sizes.
  4. Use Purify to plug memory leaks and detect memory access violations.
  5. If what you're doing seems clever, think of another way to do it.
  6. Make core dumps impossible.

PROOFREAD

Software has become a publication medium. People will read your code. Your name is on it, so you want to be proud of it. It seems that most people stop working on the code when it "works." This is like stopping work on a paper when what it says is correct, and not worrying at all about how it is said. PROOFREAD YOUR CODE AFTER YOU'VE GOTTEN IT WORKING.

ERROR MESSAGES AND COMMENTS

Use grammatically correct structure. Why say "X not foo" when you could say "X is not foo"? It's true that the former makes you sound like a computer, but wouldn't it be better to sound like a literate human being?

NAME SPACE ISSUES

All programming languages have a global namespace. In C++, it's class names, mainly. In Tcl, it's procedure names and global variables. Choose your names carefully. Never name a class "Window" or a procedure in Tcl "makeWindow" unless this class or procedure is designed to support any window at all. Use longer names that more specific, and more descriptive, like tkLogicAnalyzerMakeWindow. If you don't like long names, FINAL NOTEtake a typing class, or learn to use registers in emacs.

CHOOSING NAMES

Never name a variable x or i unless its scope is one or two lines. You cannot usefully search for these names in a text editor. Use longer more descriptive names.

Never name a variable after an arbitrary symbol. For instance, if some book you read used the Greek letter alpha for a step size, you might call your variable "alpha". Alternatively, you could call it "stepSize". Which do you think is better?

In Tcl, there is no limit to name sizes (as it should be). In some primitive operating systems, names in a link file must be unique in their first 13 characters. In a 13-character file system, the stars MDSDFFFTDecInTime.pl and MDSDFFFTDecInFreq.pl will be the same. This is too bad. Use all 13 characters wisely, and use additional characters for readability. In the previous example, MDSDFDecInTimeFFT.pl and MDSDFDecInFreqFFT.pl would be unique.

GLOBAL VARIABLES

When possible, avoid using global variables. If you have to use global variables, then make them static to the module. It is more robust and more easy to test functions if you pass all of the information as arguments to the function. Similarly, in C++ and itcl classes, do not create a new data member just to store side information that a single method will use. Instead, just pass the information as an argument to the method. This is a common violation in the code generation stars when enumerating over the ports in a multi-port hole. For example, compare the go methods in the CGCAdd and CGCAddCx stars. The CGCAdd star relies on side effects, whereas the CGCAddCx star does not. Which is easier to maintain?

Perhaps the worst violations of good programming style fall in this category of global variables. I've seen, for example, a global variable called y set in Tcl during the setup method of a star, and used during the run method. First, it's a lousy name (see above). Second, it is unlikely to have the same value by the time the run method is invoked. Third, if there is more than one instance of the star, THEN THIS VARIABLE IS GUARANTEED TO HAVE THE WRONG VALUE when the go method is invoked. The same global variable is being used to store state for two different stars!

Tcl isn't object oriented, but you can still write object-oriented code. For Tcl stars, the recommended method is to store all state in the array $starID. This array has a name set by the underlying base classes. It will not clash with any name used in any other star. To store a value in this array, just do:

	set $starID(foo) value
To retrieve the value, do:
	[set $starID(foo)]
We have been rewriting our tcl code in the object-oriented language incr tcl to hide shared variables.

ARRAY, STRING, AND DATA STRUCTURE SIZES

The old fashioned way to handle strings is to guess about their size. E.g.:
	#define STRING_SIZE 1024
	char mystring[STRING_SIZE];
	sprintf(mystring, ... );
Almost all programs that do this crash eventually. It's an axiom. The same applies to arrays. Unless you know ahead of time the size of the data structure you need, use a dynamic data structure. Tcl has a very nice infinite string structure you can use from C. In Ptolemy, use the InfString class. For arrays, once you can determine the required array size, dynamically allocate the memory (be sure to free it later):
	double* newArray = new double[arrayLen];
If the array size changes dynamically, use an unbounded list structure. You can use Purify to verify that you have deallocated any memory you allocated (see below).

DEALLOCATING MEMORY AND FINDING MEMORY LEAKS

A class should manage its own data members that are dynamically allocated. Any data members that are pointers should be initialized to zero in the constructor. The destructor should always deallocate them. This follows the rule that the class that allocates memory should be responsible for deallocating it. In some cases, that is not possible. For example, many of the classes in Ptolemy have a method to return a copy of themselves. In that cases, it is up to the caller to deallocate the memory.

Deallocating dynamic memory is a hassle, but you should always free allocated memory. Run Purify on your code to find memory leaks and memory violations. It will not be put into Ptolemy if Purify gives warnings. For more information about how to use Purify and Ptolemy together, see the Purify Section of the Developer's Guide.

HACKS

I just rewrote some code that was full of uncommented Tcl lines like this:
	set Sd [string trim $win .w]
After some puzzling over this, I figured out that the starID variable (renamed to Sd to save typing, presumably) was being parsed from the window name, which had the form .w$starID. Is this clever or dumb? In this case, probably both. The starID should have been passed as a parameter into the procedure. The above code would break if a simply and innocent change was made in the window name, such as changing it to .win$starID.

DEFENSIVE PROGRAMMING

If someone else is going to use your code, assume they will use it wrong. Thus, despite the fact that your documentation clearly states, for example, that the number of labels should equal the number of inputs, CHECK IT. Dumping core is simply not an acceptable error message.

CORE DUMPS

Any code that dumps core under any circumstances is not ready to be sent out to the world. It is possible to write crash-proof code. It is also possible to test your code. If during testing it crashes, find the reason. Crashes are not caused by gremlins. They are caused by faulty code. Use the debugger. It's easy to use and has on-line help.

FINAL NOTE

If you write bad software, and are very lucky, someone will rewrite it for you and you will still get credit for it. Only the person who rewrote the code for you will know just how badly you did. More likely, however, nobody will find the time to rewrite it, and your software will either be thrown out (if someone reads it) or published (if nobody reads it). The latter is the worst possible scenario for you. We have had to discard entire domains because they were so badly written.