Saturday, June 12, 2004

In praise of continue



Advanced C++ Programming Styles and IdiomsI've read some fairly daunting software engineering manuals that discourage -- or even ban altogether -- the use of a 'continue' statement. This, in my experienced but fallible opinion, is a complete mistake. I will illustrate to you, using actual code (no, seriously?), why I believe this is so.

I've abbreviated this code to make it easier to follow. It resides in the heart of the BadBlue scheduler, which mimics Outlook's recurring appointments feature. One of the neat things about the scheduler is that it allows recurring events, no matter how many times it occurs, to be stored in a single record. So if you have an 8:00am daily appointment with your therapist, Monday through Friday, the cost is a single database record.

This code is abridged from the method that finds tasks in a specific "window" of time (a begin-date and end-date). Virtual, recurring events are extrapolated from the physical records that serve as their instantiator. This code finds events (real and virtual) in an ordered list and, if they fall into the window, saves the event ID for the caller's use.



// Set up a search loop until we find something in...
// the find-window (or we run out of items).
//
while (m_posFind != NULL) {

// Get next item in ordered list...
//
if ((pstrKey = m_tplResultSet.GetNext(m_posFind)) == NULL) {
continue; // 1
}
strKey = *pstrKey;

// Does a delete record override this item? If so, skip it.
//
if (m_mapDeleteSet.Lookup(strKey, strJunk)) {
continue; // 2
}

// Find the real key: key contains date/time + taskID.
// ...content contains caption, duration, recursion-flag.
//
if (!m_mapResultSet.Lookup(strKey, strContent)) {
continue; // 3
}

// Decode key...
//
yr = mon = day = hr = min = sec = 0;
sscanf(strKey.GetBuffer(0), "%d-%d-%d %d:%d:%d %ld",
&yr, &mon, &day, &hr, &min, &sec, &dwTaskID);
CDateTime odtItemStart(yr, mon, day, hr, min, sec);

// Decode content...
//
bufferCaption[0] = '\0';
yr = mon = day = hr = min = sec = 0;
bufferIsRecurrence[0] = '\0';
sscanf(strContent.GetBuffer(0),
"%[^~]~%d-%d-%d %d:%d:%d~%[^~]~"
"%[^~]~%d-%d-%d %d:%d:%d~%ld~",
bufferCaption,
&yr, &mon, &day, &hr, &min, &sec,
bufferIsRecurrence,
bufferExtraInfo,
&yrx, &monx, &dayx, &hrx, &minx, &secx,
&dwExtraInfo
);
CDateTime odtItemEnd(yr, mon, day, hr, min, sec);

// Is item in find window? If not, continue...
//
if (InWindowNonRecurring(
(CDateTime) odtItemStart, (CDateTime) odtItemEnd,
m_dFindWindowStart, m_dFindWindowEnd, 0)) {
continue; // 4
}

// Store appropriate args for caller's edification...
//
dStartDate = (CDateTime) odtItemStart;
dEndDate = (CDateTime) odtItemEnd;
strSubject = bufferCaption;
bIsRecurrence = (!stricmp(SRECURS, bufferIsRecurrence));

// Found... quit.
//
bRet = TRUE;
break;

//
// ...end of loop through major window items...
//
}


Note the four continue statements, marked above.

1) Is the loop finished? If so, let the loop terminate gracefully (m_posFind == NULL)
2) Does a "delete" record override this item (e.g., has the appointment been cancelled or otherwise overriden)? If so, skip to the next item
3) Can we find more information about this event in the result set? If not, then it's been removed from the pool of valid events and we can skip to the next item
4) Lastly, is this item even in the window of interest (i.e., does it occur after the begin-date and before the end-date)?

Now imagine if this code was written without benefit of continue statements. In other words, we would use nested "if ... else ..." clauses... and nested rather deeply based upon my initial take.

Now note the relative clarity of the above logic compared to the hypothetical three or four levels of nested if constructs. To me, there's no comparison between the two approaches. I would encourage any developer to use continue constructs wherever clarity can be used to fight nested if statements.

2 comments:

Pete Lyons said...

Do you remember any of the names of the references that discouraged using 'continue'? It seems like such a bogus recommendation I would want to steer clean of them.

directorblue said...

These were coding practice guidelines related to large space, government and/or government contractor projects. There are a whole set of formal coding documents (the names/numbers escape me at the moment) that describe methods for writing ultra-reliable code. This (the 'continue') recommendation was among them.