Since this is my first article, I'll begin by introducing myself and talking a bit about what I'm doing here. My name is Toby Jones and I work for Sonic Foundry. Sonic Foundry is located in Madison, Wisconsin, which is also home to Raven Software and Human Head Studios. Sonic Foundry is known for its unparalleled sound and music software, but more recently we have moved into the video market. I'm part of that move. My current focus is writing the video capture portion of our Video Factory and Vegas Video software.
I must say, working for a real company is a lot different from writing software out of my dorm room. For one, I get to work with people that are a lot smarter than me. This is important because there is a lot of room to grow. Another thing is the access to a huge library of code, from which I can draw ideas and learn from. It's amazing.
One of the coolest things that happens when you first start is how efficient you become. Your IDE is your best friend, and it pays to know it well. So, today I'm going to talk about some of the cool features I use daily, and some features that we use at Sonic Foundry to make our products better. It's amazing that a number of the features I take for granted every day, many people don't even know about. I have a lot I'd like to talk about, so I'll move quickly. If you have a question (or criticism :) email me at firstname.lastname@example.org. So lets begin.
Tip 1) Reduce indentation to enhance readability.
First, lets talk about coding style. About a month ago, there was a tip on flipcode on how to reduce code indentation. The problem was multiple nested 'if' statements (though any flow control construct has the same problems). So you have:
Tip 2) Move lvalues to the right side when possible
Do you ever have code that looks something like:
Tip 3) Use character set independent code
Another thing we do is code for multiple character sets. Our code will compile clean under Unicode, ASCII, or even multi-byte character sets. Some say that this is not important. The fact is, code that can do this is better abstracted, and generally, just better code than code that is character set specific. So how do you do this you say?
An ASCII string is declared in C like:
Tip 4) Have your compiler generate browse information
Enough code tricks for one day. Lets talk about your IDE. I use Visual C++ daily, and I wouldn't switch for the world. It is very good, if you know how to use it. One feature that I cannot live without is browse information. Browse information is not turned on by default, so you will have to turn it on manually. If you can afford a slightly longer build time (you can, its worth it), then enable browse information by going to Project->Settings, and checking the "Generate Browse Info" box under the C/C++ tab.
Next time you compile, you will have all sorts of cool features. Right click on a function name, and you will have the option of going right to where that function is declared. This is a _big_ help in multi-module projects. Alt-F12 on a function name can bring up a graph of functions that call or are called by the highlighted function.
Tip 5) Use macros in your Watch window
Sometimes we call Windows APIs, but don't call GetLastError to check the error code. If we are debugging this code, we have no way to know why a function failed.
Type "@err,hr" in your Watch window (without the quotes). @err is a macro that calls GetLastError. The "hr" formats the macro as an HRESULT. Now your error strings will appear in your Watch window.
Tip 6) More Watch window fun
Another mistake I make is not checking my return codes. I hate to have to recompile just to test one while debugging. The Watch window can help here too.
Besides @err, there are several processor registers and pseudo registers you can use in the Watch window. Since most C functions return their result in the EAX register, I can type @eax in the Watch window. So if I don't assign the function to a variable, I can still view the value this way.
Any processor register can be viewed in the Watch window by prefixing it with @. VC6 also has a few pseudo registers that you can access. One of them is @err, which we already saw. Others include @clk (a clock register of sorts) and @tib (thread information block, NT/Win2K only).
Tip 7) Learn your keyboard shortcuts
Keyboard shortcuts will cut down your development time significantly. Here is one that I haven't seen documented.
If you have large blocks of #ifdef code, you can jump between them with Ctrl+J (jump forward) and Ctrl+K (jump backward). Positioning the cursor on an #ifdef/#if, and pressing Ctrl+J will take you to the corresponding #else/#endif. This is very handy when you have multiple nested #ifdefs.
Tip 8) The undocumented autoexp.dat file
An undocumented feature is the autoexp.dat file. In your MSDev directory is a file that holds all kinds of internal configuration parameters. Jay Bazuzi of Microsoft calls the file self-documenting, but he gives some great uses for it in his Power Debugging seminar. One of these seminars can be found at http://msdn.microsoft.com/library/mmedia/9-324.asx
Tip 9) Rewrite DebugBreak to make life easier
Other handy devices are to rewrite your DebugBreak and assert macros. Since Windows is now completely x86 dependent, there is little reason not to do this. DebugBreak is a Windows API that stops execution when called. This is fine, except DebugBreak is a function call. This means that you have to step out of the function before you can start working. What would be nice is if we could break at the point of execution of DebugBreak instead of inside the function.
Under x86, we can replace the DebugBreak function with a macro:
Tip 10) Roll your own asserts
Asserts come in two flavors: runtime and compile time. Most people are familiar with runtime asserts. These asserts simply stop execution when an expression is false. By redefining your runtime assert, you can make it a bit more powerful. Here's one:
Tip 11) Compile time asserts
Most people aren't as familiar with compile time asserts. Compile time asserts evaluate a constant expression to make sure it is valid. If the expression is invalid, the module won't compile. This might be useful if, say, you had a structure that you wanted to assure was a certain size when compiled. Something like this:
I originally wanted to cover some thoughts on optimization as well as memory leak tracking. I'm still researching some of the material I wanted to discuss, and we are getting a bit long today, so we'll wrap it up. My main point is that most people don't come close to taking advantage of the inherent power of their IDE and support tools. Everything I've discussed today I use regularly, and the people I work with do too. It's amazing what tools are at your disposal. Some of my favorite tools I use I only learned about in the last year: Spy++ (VC6), Depends (VC6), GraphEdit (DirectX Media), DXETool (DirectX Media), and others. Play around with these things. You may be surprised at the power you have.
"If you know your compiler, and you know yourself, you need not fear the outcome of a thousand battles." -Sun Tsu