* Öö Tiib, on 22.08.2010 04:09:
"Exit" is a special command because it needs handling at the
level that calls 'doCommand'. That level has to stop looping.
In your code you're thinking about 'doCommand' perhaps asking
the user to confirm, but you stop looping anyway...
Since we don't have the actual program specification, it's hard
to say what the exit command has to do
. Given Öö's premise
that it requires some additional actions, and may result in not
actually exit, means that processing it in the doCommand()
function is a reasonable solution. And that you then need some
means of doCommand to signal that we should terminate. One
could also imagine several different commands which trigger
termination.
None of which was implied in the original example. The pattern
is a classic one for servers:
while ( ! terminationRequested ) {
Request rq = getRequest();
processRequest( rq );
}
Processing the request may result in terminationRequested being
set. Or the server may have some other means of setting this
variable. (On many systems I've worked on, terminationRequested
was a volatile sigatomic_t, set in a signal handler.) While this
is the pattern I've almost always seen for servers, I suspect
that it is appropriate for many other types of applications as
well. Still, IIUC, it wasn't the pattern you had in mind.
So, you can either special-case the signature of 'doCommand'
to support "Exit", or you can just handle it directly.
Special-casing is problematic because there can be two or
three such special cases.
It's the KISS principle, Keep It Simple.
You might alternatively consider the weaker example
cout << explanation << endl;
for( ;; )
{
double const number = numberFromUser( "Number? " );
if( number == 0.0 ) { break; }
sum += number;
}
One thing: *if* you want to defend this way of writing things,
the line containing the break must be immediately visible.
In some other language, one might imagine:
double sum = 0.0;
cout << explanation << endl;
LOOP
double number = numberFromUser( "Number? " );
UNTIL number == 0.0;
sum += number;
END
I don't think it's necessary, and it isn't as simple to reason
about (since you have different invariants before and after the
UNTIL), but at least it's visible.
But in a way it accentuates the issue because here it's
a least plausible to put both the input operation and checking
as a 'while' loop condition.
To my eyes the 'while', with side-effect condition, obfuscates
what the effect of the code is,
Totally agreed. The idiom is ubiquious with iostream, so we're
sort of stuck with it, but we don't want to extend it to other
things. As a rule, you should be able to follow the general
outline of the code by just scanning down the left side of the
code. Hiding state changes in the conditional of a while is
just obfuscation.