views:

375

answers:

7

Recently we found a "good way" to comment out lines of code by using continue:

for(int i=0; i<MAX_NUM; i++){
  ....
  .... //--> about 30 lines of code
  continue;
  ....//--> there is about 30 lines of code after continue
  ....
}

I scratch my head by asking why the previous developer put the continue keyword inside the intensive loop. Most probably is he/she feel it's easier to put a "continue" keyword instead of removing all the unwanted code...

It trigger me another question, by looking at below scenario:

Scenario A:

for(int i=0; i<MAX_NUM; i++){
  ....
  if(bFlag)
    continue;
  ....//--> there is about 100 lines of code after continue
  ....
}

Scenario B:

for(int i=0; i<MAX_NUM; i++){
  ....
  if(!bFlag){
  ....//--> there is about 100 lines of code after continue
  ....
  }
}

Which do you think is the best? Why? How about break keyword?

+14  A: 

Using continue in this case reduces nesting greatly and often makes code more readable.

For example:

for(...) {
    if( condition1 ) {
        Object* pointer = getObject();
        if( pointer != 0 ) {
            ObjectProperty* property = pointer->GetProperty();
            if( property != 0 ) {
        ///blahblahblah...
        }
     }
}

becomes just

for(...) {
    if( !condition1 ) {
        continue;
    }
    Object* pointer = getObject();
    if( pointer == 0 ) {
        continue;
    }
    ObjectProperty* property = pointer->GetProperty();
    if( property == 0 ) {
       continue;
    }

    ///blahblahblah...
}

You see - code becomes linear instead of nested.

You might also find answers to this closely related question helpful.

sharptooth
nice... I like your example on the benefit of using continue. It is similar to preferring a switch over many if()s.
Alexander Rafferty
Except in most cases, if you have many if statements, a lookup table of function pointers would be clearer than a a switch, and easier to read and maintain.
haylem
Clang coding guidelines also favor the "early return" style to reduce indentation: early return, extensive use of continue / break, etc...
Matthieu M.
Excellent example - very hard to argue against this usage.
JBRWilkinson
+2  A: 

Think on readability first, which is what is going to make your code more maintainable. Using a continue statement is clear to the user: under this condition there is nothing else I can/want to do with this element, forget about it and try the next one. On the other hand, the if is only telling that the next block of code does not apply to those for which the condition is not met, but if the block is big enough, you might not know whether there is actually any further code that will apply to this particular element.

I tend to prefer the continue over the if for this particular reason. It more explicitly states the intent.

David Rodríguez - dribeas
Good thought: + 1
Chubsdad
I actually disagree with this (most of the time). While the continue statement is clear, if presents a rupture in the flow of the program that makes it harder to follow, whereas excluding if statements are easier to process mentally. It would add some levels of scoping, but as mentioned by Péter Török, then you most likely want to extract the incriminated portion of code.
haylem
+4  A: 

That "comment" use of continue is about as abusive as a goto :-). It's so easy to put an #if 0/#endif or /*...*/, and many editors will then colour-code the commented code so it's immediately obvious that it's not in use. (I sometimes like e.g. #ifdef USE_OLD_VERSION_WITH_LINEAR_SEARCH so I know what's left there, given it's immediately obvious to me that I'd never have such a stupid macro name if I actually expected someone to define it during the compile... guess I'd have to explain that to the team if I shared the code in that state though.) Other answers point out source control systems allow you to simply remove the commented code, and while that's my practice before commit - there's often a "working" stage where you want it around for maximally convenient cross-reference, copy-paste etc..

For scenarios: practically, it doesn't matter which one you use unless your project has a consistent approach that you need to fit in with, so I suggest using whichever seems more readable/expressive in the circumstances. In longer code blocks, a single continue may be less visible and hence less intuitive, while a group of them - or many scattered throughout the loop - are harder to miss. Overly nested code can get ugly too. So choose either if unsure then change it if the alternative starts to look appealing.

They communicate subtly different information to the reader too: continue means "hey, rule out all these circumstances and then look at the code below", whereas the if block means you have to "push" a context but still have them all in your mind as you try to understand the rest of the loop internals (here, only to find the if immediately followed by the loop termination, so all that mental effort was wasted. Countering this, continue statements tend to trigger a mental check to ensure all necessary steps have been completed before the next loop iteration - that it's all just as valid as whatever follows might be, and if someone say adds an extra increment or debug statement at the bottom of the loop then they have to know there are continue statements they may also want to handle.

You may even decide which to use based on how trivial the test is, much as some programmers will use early return statements for exceptional error conditions but will use a "result" variable and structured programming for anticipated flows. It can all get messy - programming has to be at least as complex as the problems - your job is to make it minimally messier / more-complex than that.

To be productive, it's important to remember "Don't sweat the small stuff", but in IT it can be a right pain learning what's small :-).

Aside: you may find it useful to do some background reading on the pros/cons of structured programming, which involves single entry/exit points, gotos etc..

Tony
+7  A: 

For your first question, it may be a way of skipping the code without commenting it out or deleting it. I wouldn't recommend doing this. If you don't want your code to be executed, don't precede it with a continue/break/return, as this will raise confusion when you/others are reviewing the code and may be seen as a bug.

As for your second question, they are basically identical (depends on assembly output) performance wise, and greatly depends on design. It depends on the way you want the readers of the code to "translate" it into english, as most do when reading back code.

So, the first example may read "Do blah, blah, blah. If (expression), continue on to the next iteration." While the second may read "Do blah, blah, blah. If (expression), do blah, blah, blah"

So, using continue of an if statement may undermine the importance of the code that follows it.

In my opinion, I would prefer the continue if I could, because it would reduce nesting.

Alexander Rafferty
+4  A: 

I agree with other answerers that the first use of continue is BAD. Unused code should be removed (should you still need it later, you can always find it from your SCM - you do use an SCM, right? :-)

For the second, some answers have emphasized readability, but I miss one important thing: IMO the first move should be to extract that 100 lines of code into one or more separate methods. After that, the loop becomes much shorter and simpler, and the flow of execution becomes obvious. If I can extract the code into a single method, I personally prefer an if:

for(int i=0; i<MAX_NUM; i++){
  ....
  if(!bFlag){
    doIntricateCalculation(...);
  }
}

But a continue would be almost equally fine to me. In fact, if there are multiple continues / returns / breaks within that 100 lines of code, it is impossible to extract it into a single method, so then the refactoring might end up with a series of continues and method calls:

for(int i=0; i<MAX_NUM; i++){
  ....
  if(bFlag){
    continue;
  }
  SomeClass* someObject = doIntricateCalculation(...);
  if(!someObject){
    continue;
  }
  SomeOtherClass* otherObject = doAnotherIntricateCalculation(someObject);
  if(!otherObject){
    continue;
  }
  // blah blah
}
Péter Török
+0.5 for suggesting to refactor and extract to another method, and +0.5 for suggesting the !bFlag clause over the use continue.
haylem
+1 for suggesting the use of functions. Functions must be **short**, readable, understandable, and with a clear name.
Stephane Rolland
@Stephane: functions like those proposed are often only called from one place, that the name be short is a low priority. @Péter: those if/continues are still pretty ugly :-)... if only C++ allowed multiple variable declarations inside an if, rather than just one, then we could use `if ((SomeClass* someObject = ...) and (SomeOtherClass* otherObject = ...)) { ... }` or equivalently `if (!(SomeClass* someObject = ...) or !(...)) continue;`. As is, can consider delocalised `SomeClass* someObject; SomeOtherClass* otherObject;` then `if (!(someObject = ...) or !(otherObject = ...))`. All taste.
Tony
@Tony, it's not the function name but the function **body** which should be short :-) To me, readability is a prime concern overall. That a function is called from only one place should not be an issue most of the time - unless you can prove that it causes a performance problem. And then you still have `inline`.
Péter Török
@Stephane / @Péter: my apologies... complete misreading of Stephane's comment there. Yes... short functions are good, short identifiers not necessarily. In mentioning the function may be being called from only one place, I was just thinking that it's not a problem to have to type a longer function identifier. I think we're all in agreement... I just didn't realise that earlier :-<.
Tony
@Tony, no pb :-) Even when the code is called only once, I still make functions. Each one can be debugged thouroughly, and I can rely on them later with almost no questionning. With big functions, there can always have interraction between the start and the end below out of the screen. And one more point, making short functions with clear name does some sort of code documenting itself.
Stephane Rolland
+3  A: 

I hate comment out unused code. What I did is that,

I remove them completely and then check-in into version control.

Who still need to comment out unused code after the invention of source code control?

Yan Cheng CHEOK
I'd agree with that in most cases, but with a reserve... If you don't keep an eye on your SCM regularly, you may miss the disappearance of some not-so-unused code and then take a while to figure it out and realize you need to check for earlier versions. What I usually do is first commenting-things out with a task-tag (TODO, TOREVIEW, TODELETE), a name-tag (to track the author without looking at the SCM) and timestamp (same reason) with a rationale for commenting things out. Then the next time I skim over this piece of code, and if nothing was brought up in a meeting about it, I remove it.
haylem
@haylem, many modern IDEs can detect unused code. Although in C++ it may not be as reliable as e.g. in Java, it is still an aid. Also, when you see such code, why not ask one (or more) fellow developers right away to confirm it can be safely removed? And last (but not least), with a good set of unit/integration tests ran automatically after each commit, you can be much bolder :-)
Péter Török
I obviously try to ask my fellow developers, but it's not unusual that it is something implemented more than 5y ago and by someone who left the company, and in some cases we don't even know who it was because of codebase migrations gone wrong between SCMs. Otherwise, I'm with you on the principle to keep it quick and clean.
haylem
+3  A: 

Continue is useful in a high complexity for loop. It's a bad practice in using in commenting out the remaining code even for temporarily debugging. Since people tends to forget...

Sam