On Oct 5, 2013, at 4:30 PM, "Pete Bartlett"
Rob Stewart:
If you violate a precondition, and the function scribbles on memory, for example, what do you >know about the state of your system? Can you do anything safely? Everything you do from that >time may be wrong, despite your hopes to the contrary.
I think this is the absolute crux of it, for me. So often I hear "if XXX happens then all bets are off, there's nothing better than going to the command line, directly to the command line, do not pass go, do not collect $200."
I'm not sure what the command line reference is about, but the point remains. If you didn't write the function you're calling, then you don't know how it will react when you violate its precondition now or in a future version. If you dislike this, use a wrapper that throws, as I mentioned previously.
But my real-life experience is just not like that. What really happens in practice is that some under pressure programmer has forgotten the one code path that means p is NULL and so we can't construct a Widget.
At that point, if you use p to create a std::string, what do you expect to happen? It's UB. It can do anything to your program or system. What we're discussing is no different.
That's what I know about the state of my program, that I can't construct a Widget when I really want to. So I throw sufficiently far out of the program until it isn't necessary to have a Widget. And there's always some layer of the program (even it is the end of main) where that's the case.
You can't throw an exception if you don't write a check for a null pointer. If you do, then you've decided that your function should have different semantics than what we're discussing. We can disagree with the use of an exception in some particular case, but I'm not going to say you shouldn't do what you've described.
To give a concrete example, suppose I want to carry out 1,000,000 "simulations" of some sort that take all night to run. E.g. the simulations returns a number and the output of my program is some statistics about those numbers (e.g. the mean). The simulations are defined by some scenario description language that gets parsed. In 50 of the simulations, those definitions mean I go down a code path that has the bug. In the other 999,950 definitions, I get the right answer. If I assert, then I lose all information about all scenarios. If I throw, I get all the information I need about 999,950 scenarios, and an error log telling me that a program bug needs to be fixed before the other 50 can be evaluated. The latter is, in practice, much more useful.
The error is certainly exceptional, as presented, and clearly, you've decided that a null pointer doesn't violate the precondition. You've decided that the function has well-defined behavior in that case.
By "doctrine", I was trying to invoke some thing like "established best practice of the C++ community at large". In every other case I can think of, learning about an established best practice has improved the programs I contribute to. But the assert thing I still don't really believe and I've known about it for 10 years now, so I thought I'd mention it despite dragging things off topic - sorry for that.
Preconditions are for avoiding responsibility for situations you are specifically choosing not to handle. The reason may be because doing otherwise would be costly, it might be because faulty inputs are considered unlikely, etc. If you don't want to deal with things that way, then don't. ___ Rob (Sent from my portable computation engine)