Well, that's a generalization:
It's a relativisation. The original arguments in favor of SESE
(e.g. by Dijkstra et al.) did not involve cleanup code. That
doesn't mean that cleanup code isn't important in practice.
Just that it wasn't the original motivation (and that even when
cleanup code isn't an issue, e.g. when you are using RAII, the
original arguments hold).
cleanup code is just the most important
in practice, because maintainance is more important than original
development. You may be one of the lucky people who has not had to help
maintain C systems with 600 to 1000 line functions, evolved haphazardly.
I have. In such cases, there is only one solution: rewrite the
code with smaller functions.
I can assure that you such systems are very common, for you see, most
programmers are average. And the average programmer who maintains such
code has a tendency to introduce early returns that skip cleanup code
(often this is later corrected by duplicating the cleanup code in
question, which then gets out of synch, and so on and on).
The problem isn't just the programmers. "Average" programmers
can write very good code, if they are managed correctly. Good
code review, and coding guidelines, for example.
You're IMO right that it's best to not hide exits deeply nested in
masses of other code, but C++ code must be /prepared/ to exit any point.
It depends. I tend to prefer a transactional style, which
verifies first that nothing can fail, before modifying any
actual data. (But of course, it isn't always possible.) In
practice, no throw guarantees are important for many idioms.
[...]
"Invariants" is not really meaningful here.
Invariants are always important.
The problem here, of course, is that we have an abstract
function, without any idea as to what it really means. So we
don't know what invariants are relevant. The important point is
that the structure of the code (indenting, etc.) very strongly
suggests that the results of pointer2->doSomething1() may in
fact hold, where as it is actually guaranteed that they don't.
The indenting is a lie.
But at the point you
indicate, you can assert(pointer1!=0), so that's an invariant of sorts.
Also, at that point pointer1-doSometing() has been executed.
It's the first I was thinking of. The indentation and program
structure says one thing. (We're after the if, so the condition
of the if is no longer guaranteed.) The actual situation is
another. The indentation is lying.
Of course, the original author, when writing the code, is very
much aware of the preceding return, and the additional
guarantees it introduces. So he writes code which takes
advantage of them. The later maintenance programmer then has to
try to figure out how it can work, when the invariant on which
it depends apparently isn't guaranteed.
I wouldn't call that clear, rather completely /misleading/: judging from
the original code, this function succeeds even when it does nothing.
Who knows? I just guessed at a semantic signification for the
bool. The whole function is, as you originally pointed out,
problematic. It's very hard to discuss what is reasonable in a
solution without knowing what the problem is.
But using a result variable has its charms.
Yes. It allows naming the condition. This is very important
when dealing with bool, since the name is all you really have to
go on. (It's a totally different issue, but in my experience,
it is very, very rare that a function should return bool. But
there are obvious exceptions: just about any function whose name
begins with "is", for example, or something like "contains(key)"
on a map.)
It allows allows the visual structure to correspond to the
actual control flow; code which is outside of a block controled
by an if doesn't depend on that if.