views:

127

answers:

3

Edit: Works perfectly in debugger now, but block doesn't rotate at all when run normally..

I'm having a problem that I've run through the debugger a ton and have narrowed it down to this.

I've got a block on screen that comes down in the middle and rotates. The image of the block obviously changes depends on the rotation, and this is done in a switch statement.

switch ( m_CurrentRotation ) {
  case BossRotation_ZeroDegrees: {
    ApplySurface(
      m_BossRect.topLeftX,
      m_BossRect.topLeftY,
      BossFiveImage::p_ZeroDegrees,
      p_Buffer
    );
    break;
  }
  case BossRotation_NinetyDegrees: {
    ApplySurface(
      m_BossRect.topLeftX,
      m_BossRect.topLeftY,
      BossFiveImage::p_NinetyDegrees,
      p_Buffer
    );
    break;
  }
  case BossRotation_OneEightyDegrees: {
    ApplySurface(
      m_BossRect.topLeftX,
      m_BossRect.topLeftY,
      BossFiveImage::p_OneEightyDegrees,
      p_Buffer
    );
    break;
  }
  case BossRotation_TwoSeventyDegrees: {
    ApplySurface(
      m_BossRect.topLeftX,
      m_BossRect.topLeftY,
      BossFiveImage::p_TwoSeventyDegrees,
      p_Buffer
    );
    break;
  }
  default: {}
}

The block enters at zero degrees, and once it gets in the middle it starts rotating. I've found from debugging that in the switch statement, the ApplySurface for the FIRST case isn't being called (when I try to step into it, nothing happens). This causes the block to go "blank" every time it gets to that rotation point.

Here are the odd things...

1) If the ApplySurface function isn't being called, then why can you see the block coming down at first (before it starts rotating)?

2) Running the program in the debugger and running it normally show different results. Normally, it just shows the block in its zero degree position the whole time. The debugger is the only time it actually attempts to rotate the block. Are there compiler optimizations going on that are preventing whatever is going horribly wrong in my switch statement from happening?

+4  A: 

Your code seems unnecessarily long and repetitive. It should be refactored:

image = NULL;

switch ( m_CurrentRotation ) {
  case BossRotation_ZeroDegrees: {
    image = BossFiveImage::p_ZeroDegrees; break;
  }
  case BossRotation_NinetyDegrees: {
    image = BossFiveImage::p_NinetyDegrees; break;
  }
  case BossRotation_OneEightyDegrees: {
    image = BossFiveImage::p_OneEightyDegrees; break;
  }
  case BossRotation_TwoSeventyDegrees: {
    image = BossFiveImage::p_TwoSeventyDegrees; break;
  }
  default: {}
}

if (image) {
    ApplySurface(
      m_BossRect.topLeftX,
      m_BossRect.topLeftY,
      image,
      p_Buffer
    );
}

This simpler code should also be easier to debug. What is the type and the possible values of m_CurrentRotation?

Mark Byers
Instead of using image == NULL as the test, you should set a boolean flag to true before the switch and have the default case set it to false. That more accurately reflects the original code. But otherwise you are absolutely correct, the code should be refactored in a manner similar to what you suggest.
Omnifarious
@Omnifarious One argument I can see against this is that suppose the OP was debugging and got to this function. If he tested for NULL, then he would know that one of the images he tried to assign was NULL, and thus that he probably made a mistake of typing = instead of == somewhere. Using the boolean test, he only knows that none of the cases were met. Thus you have testing for what's inside the cases vs. testing for whether any of the cases were met. Not quite the same, and I think I'd prefer the first. Alternative: You could test for both.
trikker
I agree with your last sentence actually - I thinking testing for both (that a non-default case clause was executed, and that the image is not null) is probably the best solution here. The refactoring I demonstrated combines these two things into one test, which would hide an image being null simply by not displaying it - perhaps program termination would be better. Either way, the refactoring is makes things a lot clearer.
Mark Byers
A: 

Mark Byers answer is great - one of the first things I would have indicated style-wise: too much repetition of code that's almost identical. Mark's response does have one potential bug, in that he assumes that any of the valid values for 'image' are not the same as NULL. Since I don't know what kind of think BossFiveImage::p_ZeroDegrees is, I don't know whether checking if (image) will also skip the function call.

If making that change doesn't help, there are a few things I've come across, which will almost certainly depend on the compiler:

  1. uninitialized variables are sometime zeroed when code is compiled in debug but not in release mode (certainly happens with all the Microsoft c++ compilers I've used). This can cause different behaviour. They don't need to be pointers, it impacts all variables.

  2. Perhaps you've got some exception handling happening and the function call doesn't happen because of an exception - execution might continue fine if the exception is handled, but if any of the things you try to pass to ApplySurface() throw an exception you may find that nothing useful happens.

Arunas
A: 

Lots of times debugging switch statements results in the instruction pointer looking like it's on a line that actually isn't relevant to the program flow.

I'll bet you have a m_CurrentRotation that doesn't match anything in the switch statement and that's why nothing's getting called. Can you set a watch on that, or add some debug code to the default clause?

Moishe