Monday, March 3, 2008

#if DEBUG

C# allows you to use various Preprocessor Directives (the things with the # in front of them).

One of these is #if, and there is a DEBUG symbol you can use to determine if the program is currently running in debug mode or release mode. In general, using this is an awful awful thing to do. Why would you want to modify the behavior of your application when it was in debug mode vs. release mode?

Well, you might want to do this if you needed to add some code in debug mode to make debugging easier for yourself. But I can't imagine a case where you would want to use it to actually change the behavior of your application.

Why? Do you really need me to tell you why? Okay, fine. Its because now you can't trust debug mode. Any testing you perform in debug mode no longer applies to release mode. You might as well never run the app in debug mode, its a complete waste of your time.

"Surely you're overstating the issue!" protests John Q Programmer. "Like, what if I wanted to throw an exception if something was misconfigured or misused, but I only wanted it to blow up in Debug mode. In release mode, I don't want the application to blow up!"

Not wanting the application to blow up in release is very noble of you, John. I commend you. You are truly wise beyond your years. But I'm assuming you, Mr. Programmer, are throwing the exception because something has gone wrong. NOT throwing the exception in release mode is not going to change the fact that something has gone wrong. In fact, it is likely to make it worse. J.Q.'s application may now be in an inconsistent state! There's no telling what might go wrong. John hasn't avoided a problem in his application. Far from it! He's just potentially made the problem 100% times worse.

So J.Q., please don't do that.

Now, if you really must use preprocessor directives for some reason, at least make sure you're VERY careful with them. It may look like an if statement, but it's not. It is literally changing what code the compiler gets to see. If the #if doesn't pass, it will be as though the lines of code within it never existed.

That means you can get some really nasty behavior if you're not careful:
if ( iNeedToDoSomething)
DoSomething();
else
#if DEBUG
DoSomethingElse();
#endif

DoTheMainThing();

See what's wrong with this?

If we are in debug mode, this code looks like:
if ( iNeedToDoSomething)
DoSomething();
else
DoSomethingElse();

DoTheMainThing();

If we are NOT in debug mode, this code looks like:
if ( iNeedToDoSomething)
DoSomething();
else
DoTheMainThing();

Holy crap! DoTheMainThing just got moved into the else! But only when we're not in debug mode.

To fix this you could simply add curly braces, but I think this example demonstrates how easy it is to get preprocessor directives messed up. And also why you should try just about anything to avoid using them.

4 comments:

  1. That John Q. He is sooo crazy!

    Nice post though ... Java doesn't have anything similar to my knowledge and I had completely forgotten about the construct since our C++ days in college.

    ReplyDelete
  2. Interesting, you're right. Java doesn't have a preprocessor.

    Probably another one of the things that they removed because it was "unsafe." In this case, I'm totally on board with that.

    ReplyDelete
  3. You know, just about the only thing i could see using this for would be tracing code while debugging, but that's flawed cause you have a perfectly good debugger.

    Multi-threaded apps can be a bit of a pain to debug sometimes, so maybe it's helpful there? Hell i don't know i think your right and its dumb but im trying to come up with something.

    ReplyDelete
  4. being able to send metadata to the compiler seems like it might have its uses (various optimizations routines, different forms of output, etc) .... but it doesn't seem like something that should be included within the code. You would hope that the same code, compiled at different times would result in the same bytecode/assembly/etc.

    It seems like that kind of information belongs within a build tool/environment.

    ....

    while not the same, this topic reminds me of the optional and often undocumented or not-fully-supported parameters you can send to the JVM when it starts up (e.g. this list). Yes they can be useful to tune specific settings for out of the norm situations ... but most of the time they aren't needed. Or if they are, they should be set as a default b/c who wants to have to set X, Y, and Z to make something fast. Why would I want to run something slow?

    ReplyDelete