| |||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||
Print This Article REALbasic University: Column 106
Debugging Part OneFor the past few lessons we've been looking at progress bars, and I'd planned to move on with a look at a few more common problems and how to handle them in an OOP environment. But I recently had an experience that reminded me of the complexities of debugging, and I thought a brief series on how to debug would be in order.
Complex InteractionsWhen talking about cars and mechanical problems, I like to tell the story of my old Dodge. "First the heater went," I say, "then the air conditioner. I could handle those problems initially, but the air conditioner dying eventually killed the speakers, and I couldn't handle the road without music, so I had to buy a new car." Of course that doesn't make much sense -- speakers have nothing to do with air conditioning -- so it's an intriguing statement and provokes a conversation. I usually like people to ponder this quandry for a few minutes, try to figure out what my air conditioner has to do with my speakers. To date, no one has been successful. They just think I'm nuts (which probably isn't far off the mark). But when I explain, it seems painfully simple. You see, with the air conditioner out, I was forced to roll down my windows to keep cool. That meant a lot of wind noise, which meant I had to crank up the volume to hear the stereo. Eventually that blew out my speakers! I tell this story to illustrate an important point about computers. What attracted me to programming is that it's logical: everything is ordered and works according to a predefined set of rules. But when you get into the world of debugging, it sometimes seems that order has gone out the window. Just like the air conditioner in my old car, seemingly unrelated events effect each other. Sometimes successful debugging requires thinking out of the box. Now there are sophisticated tools to help you debug your programs, but the best tool is your mind. If you can train yourself to think like a computer, you can learn to see the sometimes hidden cause-and-effect that is producing the undesirable behavior in your program. Let's look at some examples.
The Debugging ProcessThere is no magical debugging progress. Debugging is complicated: every type of bug is different and needs a different kind of process to find it. For example, the simplest type of bug is a syntax error: usually the compiler catches these when attempting to parse your source code. Here's one:
Obviously the comma is not allowed and REALbasic won't compile the application. But consider these examples that do compile but probably don't do what you want (assign the number 10,001 to a):
Syntax errors are usually typoes, but when the typo generates compilable code, it can be tricky to find the error. Usually the code produces unexpected results, which usually doesn't narrow the field much and help you figure out where the error is located. But other errors are more deliberate. For example, it's a common technique to use simple variable names like i and j for temporarily variables used in loops and stuff. But i and j aren't particularly memorable, and it's easy to write a routine where you accidently use i where you meant to use j. I just did this a few days ago in a program that processed folders and files you fed it. The outer loop was i, representing the number of folders. The inner loop was j, for each file in folder i. But the output was only showing a single file processed. I checked the input code and it was fine. That routine successfully read in a folder and all 27 items inside it. Yet the output had only one file in it! So where was the bug? I finally noticed that within my inner loop instead of refering to file j, I was referring to file i, and since i was a 1 (the first folder), only the first file was returned. A simple change and my program worked: but it took me a little bit to find the bug. An easy fix to prevent this kind of error, of course, is to use more detailed variable names. If I'd used theFolderIndex and theFileIndex instead of i and j, I certainly wouldn't have made that error. Often when you're coding, though, it seems easier to use random letters for temporary variables instead of detailed names. Partly that's because your mind isn't completely clear on how the variables will be used so it's difficult think up an approrpriate name. Forcing yourself to create a detailed variable name will force yourself to think more about what you're going to be doing -- which will only help you. Another common error happens when you copy and reuse bits of code. I'm lazy and rather than retype code, I'll copy it from a different section or another program. This can work and if the other code has been debugged, could produced more reliable code. However, you must watch out for variable names: you might have used different names in the original routine, or -- very bad -- used the same variable names but for different purposes. Fortunately, this type of error isn't as much of a problem with REALbasic because RB's auto-complete feature makes typing code in fresh so easy it isn't hardly worth copying. Of course these are the simpler typo-type errors. There are many other kinds of errors you can make, and we'll look at some of those next time as we continuing to debug debugging. Next WeekMore debugging. LettersToday we've got a question from John about keycodes versus ASCII characters, among about fifty other puzzlers.
Wow, John, you're really opening a can of worms here! I'll do my best with the main question, then tackle the others. First, note that modifier keys (Shift, Control, Command, Option/Alt, Function [PowerBook's have these]) are not available in ASCII. These are not characters -- they only modify the character typed (i.e., Shift capitalizes a letter). So the only way to test them is to ask the system. In RB, this is done with REALbasic's keyboard object. Note there are two types of methods in the keyboard object: the async commands tell you if the modifier is press right now, while the regular commands tell if you the key was pressed when the current routine (method, event, etc.) was called. This is important, particularly in a program like SuperDraw, where you might want to know the state when the mouse button was pushed, or you might prefer to know the current state. Now let's tackle key codes. These are completely different from ASCII or other character representations. Key codes are special numeric representations of every key on a keyboard. Because of this, while the numbers are the same for the same keyboard position, different operating systems and language keyboards (i.e., U.S. QWERTY versus French or Spanish keyboard, etc.) put different letters in different places. For instance, the key to the left of the Tab key is "Q" on the U.S. keyboard and "A" on the French keyboard, yet they both have the same key code of 0C. Since you have no way of knowing what keyboard a user is using, it's difficult to use these key codes for practical purposes. Unfortunately, that's what the keyboard.asynckeydown command returns. As you mentioned, there's a chart of keycodes in RBU lesson 20. That's the U.S. chart I copied from the RBD manual. As to your idea of creating your own keyboard graphic with the keycodes inside it, it could be done, but it'd a fair amount of work. You could take an existing keyboard graphic and open it in a painting program to erase the labels and then programatically add your own when the program runs. But again, you can't know what keyboard the user has connected. (In theory, with USB, the user could have multiple keyboards connected.) I'm also not sure how useful it would be do this since key codes are not used very often. Moving on to your other questions, let's first look at the improvements to SuperDraw you were talking about. To add the Shift-key extend-selection feature, you'd want to check for keyboard.ShiftKey within the mouseDown event. You don't need to check .asyncShiftKey because you aren't concerned with the current state: you want to know the state of the key when the mouse was clicked. Once you've determined if the Shift key was down or not, you could set yourself an object property (not local to mouseDown) to true or false to match. Then you could use that within mouseUp and the other routines to extend or not extend the selection. As to the logic of how extending a selection should work (i.e., what happens if the user shift-clicks on the current object, etc.), I would direct you to play with other drawing programs and see how they do it, and makes SuperDraw work the same way. Finally, you asked about the pen size, and you are correct: if you wanted full control over the pen you'd need to break the pen size into two variables to set the pen's width and height separately. That can let you draw some interesting calligraphic shapes! About the Column REALbasic University is a weekly instructional column on programming with REALbasic and is brought to you by REALbasic Developer, the magazine for REALbasic programmers. Each week we answer select reader questions, and we're always open to ideas for future columns. Send your questions to . (Keep your questions simple and specific. General queries like "How do I write my own web browser?" will be neglected.) Your question won't be answered immediately, but will be answered in a future column. (If you don't want your correspondence published, just be sure to indicate that when you write. Otherwise it's fair game.) About the Author See the REALbasic University Archives
REALbasic University contents ©2001-2004 by Marc Zeedar and REALbasic Developer. All Rights Reserved.
| |||||||||||||||||||||||||||