Next: C++/CLI limitations?
From: Peter Dimov on 23 Aug 2005 02:43 Gerhard Menzl wrote: > David Abrahams wrote: > > > No, it ceases to be a precondition the moment that the author of the > > function *documents* the throwing of that exception as the function's > > response to the condition being violated. > > > > (**) It's inconsistent to say, "This function has X as a precondition. > > However, if X is violated, I promise you it will throw an exception > > and do nothing else." The correct thing to do if you are going to > > make that promise is drop the first sentence. > > Originally, I took you to mean that throwing an exception upon detecting > a violated precondition is always wrong. Now you seem to say that > *documenting* such an exception is inconsistent with the specification > of a precondition. In principle, yes. > These are two very different statements. The first is > about implementation, the second about interface. Or, to put it > polemically: what's wrong with throwing a ViolatedPreconditionException, > as long as I don't document it? In principle, the implementation is allowed to implement the interface as you suggest. In practice, such an implementation would lock the customers in, because they'll start relying on ViolatedPreconditionException. Whether this is right or wrong is another story. In effect, you are implementing a different interface, one without a precondition. > By the way, the term "precondition" seems to be used in meanings that > differ from the one you have sketched. In the Eiffel world (as far as I > can tell), a precondition is something that the caller must guarantee > before invoking a function. This would rule out stack integrity or other > global conditions which the caller cannot guarantee. Stack integrity is an implicit global invariant if the integrity of said stack affects the observable behavior. IOW, if 1+1 yields 3 when stack integrity is violated, then all programs implicitly assume a non-broken stack, even when the language doesn't explicitly define this as an "invariant". [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Gerhard Menzl on 23 Aug 2005 18:59 David Abrahams wrote: >>The first is about implementation, the second about interface. Or, >>to put it polemically: what's wrong with throwing a >>ViolatedPreconditionException, as long as I don't document it? > > Well, I've been through the arguments about why it's usually a bad > idea in this thread. You can just review my postings (and not only > the ones in reply to yours). Honestly, I've tried, but I still find your rationale elusive. >>By the way, the term "precondition" seems to be used in meanings that >>differ from the one you have sketched. In the Eiffel world (as far as >>I can tell), a precondition is something that the caller must >>guarantee before invoking a function. > > In my definition the caller must guarantee the precondition also, > since the alternative is undefined behavior. I don't know what I've > said that could make you think otherwise. For example: > Yes, any precondition violation could be the result of stack > corruption. An intact stack is a global condition that cannot be guaranteed a single caller. Stack corruption could be the result of a broken compiler or of user code that invokes undefined behaviour somewhere else. Both events lie outside the language. They cannot be detected reliably and portably at this level, and they are not covered by the contract between the caller and the called. From this perspective, they're more like Acts of God, to extend the law metaphor. Yet you seem to regard such global failures in the context of the contract. I find this confusing and at variance with the idea of DbC as I understand it. >>This would rule out stack integrity or other global conditions which >>the caller cannot guarantee. > > If the caller is invoking the callee other than as some expression of > undefined behavior, it can. I'm assuming that even in Eiffel, the > moment stack integrity is violated you have undefined behavior. Once > you enter undefined behavior land, all bets are off and all actions > are part of that undefined behavior. Undefined behavior means "all > bets are off" and no guarantees (not even the ones you /think/ the > caller can ensure) are really valid. A violated precondition causes undefined behaviour. That doesn't mean undefined behaviour caused at a different level (like a broken compiler) can be regarded as a breach of a local contract. >>Another conflicting use I have found is from P. J. Plauger's Editor's >>Forum in the June issue of C/C++ Users Journal where he writes about a >>new C library called "Safer C": "But any implementation of the >>function must test that its arguments meet all preconditions. The >>runtime equivalent of a diagnostic is to call the diagnostic handler. >>If the handler returns, the function then cauterizes any output >>buffers and returns an error code." > > I'm not sure that's a conflict either. To me, it is in glaring conflict with > The moment you say say, "I can do something reliably here if I detect > a violation," then the condition being violated is -- by definition -- > no longer a precondition because you're defining what the behavior > should be when it happens. -- Gerhard Menzl #dogma int main () Humans may reply by replacing the thermal post part of my e-mail address with "kapsch" and the top level domain part with "net". [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Bob Bell on 24 Aug 2005 03:58 Gerhard Menzl wrote: > David Abrahams wrote: > >>By the way, the term "precondition" seems to be used in meanings that > >>differ from the one you have sketched. In the Eiffel world (as far as > >>I can tell), a precondition is something that the caller must > >>guarantee before invoking a function. > > > > In my definition the caller must guarantee the precondition also, > > since the alternative is undefined behavior. I don't know what I've > > said that could make you think otherwise. > > For example: > > > Yes, any precondition violation could be the result of stack > > corruption. > > An intact stack is a global condition that cannot be guaranteed a single > caller. Stack corruption could be the result of a broken compiler or of > user code that invokes undefined behaviour somewhere else. Both events > lie outside the language. They cannot be detected reliably and portably > at this level, and they are not covered by the contract between the > caller and the called. From this perspective, they're more like Acts of > God, to extend the law metaphor. Yet you seem to regard such global > failures in the context of the contract. I find this confusing and at > variance with the idea of DbC as I understand it. I think the point of Dave's remark above is that any precondition, like assert(index < size()); can fail because the stack is corrupted. Testing preconditions isn't about assigning blame or deciding on cause. It's just about testing a condition that must be true, or else the function can't do what it's supposed to do. If the precondition is "index < size()", and this condition evaluates to false, the function cannot know or care why. It could be because the caller just passed in a bad value. It could be because the stack is corrupted, causing garbage to be read from index. It could be because the size() function returned garbage because its invariants are broken. It could be because of a buggy compiler that emitted garbage code. It could be because of a hardware fault. It doesn't matter which of these is the case; all that matters is that the function cannot continue if index < size(). Bob [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Gerhard Menzl on 24 Aug 2005 11:09 Bob Bell wrote: > I think the point of Dave's remark above is that any precondition, > like > > assert(index < size()); > > can fail because the stack is corrupted. > > Testing preconditions isn't about assigning blame or deciding on > cause. It's just about testing a condition that must be true, or else > the function can't do what it's supposed to do. If the precondition is > "index < size()", and this condition evaluates to false, the function > cannot know or care why. It could be because the caller just passed in > a bad value. It could be because the stack is corrupted, causing > garbage to be read from index. It could be because the size() function > returned garbage because its invariants are broken. It could be > because of a buggy compiler that emitted garbage code. It could be > because of a hardware fault. > > It doesn't matter which of these is the case; all that matters is that > the function cannot continue if index < size(). That makes sense, but the same could be said about error conditions that *are* promised to be handled. A function that throws a documented InvalidPointerException could be the victim of stack corruption or a hardware fault just as well. -- Gerhard Menzl #dogma int main () Humans may reply by replacing the thermal post part of my e-mail address with "kapsch" and the top level domain part with "net". [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Bob Bell on 24 Aug 2005 18:27
Gerhard Menzl wrote: > Bob Bell wrote: > > > I think the point of Dave's remark above is that any precondition, > > like > > > > assert(index < size()); > > > > can fail because the stack is corrupted. > > > > Testing preconditions isn't about assigning blame or deciding on > > cause. It's just about testing a condition that must be true, or else > > the function can't do what it's supposed to do. If the precondition is > > "index < size()", and this condition evaluates to false, the function > > cannot know or care why. It could be because the caller just passed in > > a bad value. It could be because the stack is corrupted, causing > > garbage to be read from index. It could be because the size() function > > returned garbage because its invariants are broken. It could be > > because of a buggy compiler that emitted garbage code. It could be > > because of a hardware fault. > > > > It doesn't matter which of these is the case; all that matters is that > > the function cannot continue if index < size(). > > That makes sense, but the same could be said about error conditions that > *are* promised to be handled. No; the difference is that failed preconditions represent undefined behavior. Error conditions that *are* promised to be handled are, by definition, well-defined behavior. It seems to me that the problem being discussed is largely one of definition of terms. As I understand it, Dave is advocating a definition of precondition that reads something like "a condition which must be true when a function is called, or else we have undefined behavior." I agree with this definition, and view it as synonymous with "a condition which must be true when a function is called, or else the function cannot continue." It sounds like you want a definition that reads something like "a precondition is a condition which is tested when a function is called; if true, the function runs as normal; if false, the function throws an exception." I don't think there's anything wrong with making a distinction between conditions which cause a function to behave one way and conditions that make a function behave another. But this emphasis ignores those conditions which lead to undefined behavior, and documenting, detecting and avoiding those conditions is _very_ important for designing and implementing a robust system. > A function that throws a documented > InvalidPointerException could be the victim of stack corruption or a > hardware fault just as well. Suppose you have a function F() that is documented to throw X when condition Y fails. Suppose you call F(), and Y legitimately fails. You don't have undefined behavior, an X is thrown, and life goes on. If, however, F() is called and Y appears to fail because some undefined behavior has occured (such as stack corruption or whatnot), well now you're in undefined behavior land, and anything could happen. The program could crash, wipe your hard disk, or throw an X. If it manages to actually throw an X, it doesn't change the fact that your program is now exhibiting undefined behavior. The key point is that your condition Y did not help you detect and avoid undefined behavior. Bob [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |