Next: C++/CLI limitations?
From: Gerhard Menzl on 8 Aug 2005 15:48 David Abrahams wrote: > You're mixing things I see as distinct. When your programs supposed > invariants are violated, I am saying there is no recovery. I guess I just find it hard to get a clear view of what exactly you mean by "precondition". You cited a definition, but that definition seems to be in contradiction to several things you said before. > Yes, you need a separate mechanism of some kind. Can you imagine a > realistic scenario where you wouldn't want that mechanism anyway? A mechanism, yes, certainly. A separate mechanism and thus duplicate logic? Not if I can avoid it. >>There are applications where Undo is part of the specification, and >>applications where it isn't. Because there are operations that >>cannot be undone, for example. You can abort a phone call, but you >>cannot undo it (although people have been known to badly wish for >>such a feature). > > Well, of course in those applications there's no way to recover the > unmodified state. So what are you worried about? I am worried about throwing the baby out with the bathwater. Specifically, I have my doubts about the underlying assumption that the effects of a precondition violations are always global and thus require shutdown. > It depends on how reliably it can be isolated from the rest of the > system. > > If it truly "doesn't matter," then whatever you've specified as a > precondition was too strong. > > Very simply: the Wiki definition says that if a precondition is > violated, the behavior becomes undefined. 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. The behavior is no longer undefined, so the condition just > describes one set of valid inputs. Hm, this is beginning to smell like circular logic to me: if it is possible to continue, it cannot have been a precondition, hence a precondition is defined as a condition the violation of which makes further execution impossible, which eventually boils down to: when you cannot continue, you must stop. Now this is something I will readily agree with, but it doesn't seem like a useful definition of precondition to me anymore, and it certainly does not match the definition you cited. What I am missing particularly is a distinction between "cannot continue execution of the operation" and "cannot continue execution of the program". The former does not necessarily mean the latter. My impression (which may be wrong) is that you restrict the term "precondition violation" to situations where the latter applies. > Perhaps more importantly, a violation of X's precondition does not > merely mean that X can't fulfill its responsibility. It also means > that something else somewhere in the program is broken. It may have > been at the bottom of some long call chain that has since returned. > So by the time you detect that X's preconditions are violated, you > really have no idea where the problem is. This a situation that may occur, but it is different from the Wikipedia definition. I find your definition of "precondition" very elusive; maybe that's the source of our disagreement. >>Suppose you write several distinct values into a database table. Your >>program logic is supposed to make sure that the same value is never >>used twice, and the database definition ensures that the database >>engine applies a second check (by using a unique key, for example). If > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > I don't know what you mean by that part, but I don't suppose it > matters. It's database speak for what std::set guarantees in contrast to std:: multiset. >>a function that operates on the data relies on the values still being >>unique after retrieval, that uniqueness is, by your (Wikipedia) >>definition, a precondition. > > Yes. > >>Yet you say it's not. > > No I don't. Why do you think so? Because you wrote earlier: > Once you understand that database integrity is not a precondition, it > becomes very clear that you need to check for corruption in certain > places and make sure that you do something sensible if you detect it. Are we wrestling with conflicting definitions of database integrity 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: David Abrahams on 9 Aug 2005 04:19 Gerhard Menzl <gerhard.menzl(a)hotmail.com> writes: > David Abrahams wrote: > >> You're mixing things I see as distinct. When your programs supposed >> invariants are violated, I am saying there is no recovery. > > I guess I just find it hard to get a clear view of what exactly you mean > by "precondition." You cited a definition, Yes, I mean precisely what that definition says. > but that definition seems to be in contradiction to several things > you said before. Like what? >> Yes, you need a separate mechanism of some kind. Can you imagine a >> realistic scenario where you wouldn't want that mechanism anyway? > > A mechanism, yes, certainly. A separate mechanism and thus duplicate > logic? Not if I can avoid it. No "duplicate logic" is needed in order to keep track of the critical cleanups. You can use good old RAII with a common base class that links itself into a chain of objects that will do their business in case of a critical failure. >>>There are applications where Undo is part of the specification, and >>>applications where it isn't. Because there are operations that >>>cannot be undone, for example. You can abort a phone call, but you >>>cannot undo it (although people have been known to badly wish for >>>such a feature). >> >> Well, of course in those applications there's no way to recover the >> unmodified state. So what are you worried about? > > I am worried about throwing the baby out with the bathwater. Okay, but that's a separate question. You brought up operations that can't be undone. What could you possibly do to roll those back using unwinding? > Specifically, I have my doubts about the underlying assumption that > the effects of a precondition violations are always global and thus > require shutdown. This isn't about the *effects* of a precondition violation, but what a precondition violation indicates: a bug in the program logic. >> It depends on how reliably it can be isolated from the rest of the >> system. >> >> If it truly "doesn't matter," then whatever you've specified as a >> precondition was too strong. >> >> Very simply: the Wiki definition says that if a precondition is >> violated, the behavior becomes undefined. 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. The behavior is no longer undefined, so the condition just >> describes one set of valid inputs. > > Hm, this is beginning to smell like circular logic to me: if it is > possible to continue, it cannot have been a precondition ^ without causing undefined behavior Correct. That's simply true by definition (the wikipedia definition). Remember, I have been encouraging you to keep your terms very clear, so the definition of the word matters. > hence a precondition is defined as Whoa; I'm not drawing any conclusions about the definition of "precondition" from the above. The above *follows* from the definition of "precondition." So the rest of what you're saying here doesn't reflect what I'm saying at all: > a condition the violation of which makes further execution > impossible, which eventually boils down to: when you cannot > continue, you must stop. Now this is something I will readily agree > with, but it doesn't seem like a useful definition of precondition > to me anymore, and it certainly does not match the definition you > cited. The definition I cited says, among other things: If a precondition is violated, the effect of the section of code becomes undefined and thus may or may not carry out its intended work. > What I am missing particularly is a distinction between "cannot continue > execution of the operation" and "cannot continue execution of the > program". The former does not necessarily mean the latter. No it does not. > My impression (which may be wrong) is that you restrict the term > "precondition violation" to situations where the latter applies. No I don't. The problem is that it's usually impossible to tell the former from the latter. >> Perhaps more importantly, a violation of X's precondition does not >> merely mean that X can't fulfill its responsibility. It also means >> that something else somewhere in the program is broken. It may have >> been at the bottom of some long call chain that has since returned. >> So by the time you detect that X's preconditions are violated, you >> really have no idea where the problem is. > > This a situation that may occur, but it is different from the > Wikipedia definition. It follows very clearly from the wikipedia definition, unless you assume that programmers are intentionally inducing undefined behavior. Since a precondition violation results in undefined behavior, and --- I hope you will agree --- undefined behavior is a clear indicator of a broken program, clearly something is broken somewhere in the program. I think I have an inkling what you're thinking about this. It's something like "Ah, but if I throw an exception, the function never executes, so I can avoid the undefined behavior by not proceeding with the function and instead initiating stack unwinding." Right? That would be wrong. First, the exception throwing is part of what the function does. If the function's documentation describes a precondition, the exception happens after entering the function, i.e. after you've proceeded to execute the "section of code" to which the precondition applies. So the exception throwing is just part of the function's undefined behavior. You can't count on it, and use the knowledge of the exception to make further guarantees about the reliable behavior of the program. The moment when the function's documentation says it throws an exception in response to some condition being violated -- so that you can rely on it throwing and make further deductions about the behavior of the program afterwards -- that exception throwing becomes part of the function's defined behavior and thus the condition is not a precondition, by definition. > I find your definition of "precondition" very elusive; maybe that's > the source of our disagreement. It's very simple; it is exactly what you'll find at http://en.wikipedia.org/wiki/Precondition. >>>Suppose you write several distinct values into a database table. Your >>>program logic is supposed to make sure that the same value is never >>>used twice, and the database definition ensures that the database >>>engine applies a second check (by using a unique key, for example). If >> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >> I don't know what you mean by that part, but I don't suppose it >> matters. > > It's database speak for what std::set guarantees in contrast to std:: > multiset. I understood that; I just don't see what kind of "second check" you can get from that guarantee. >>>a function that operates on the data relies on the values still being >>>unique after retrieval, that uniqueness is, by your (Wikipedia) >>>definition, a precondition. >> >> Yes. >> >>>Yet you say it's not. >> >> No I don't. Why do you think so? > > Because you wrote earlier: > >> Once you understand that database integrity is not a precondition, it >> becomes very clear that you need to check for corruption in certain >> places and make sure that you do something sensible if you detect it. > > Are we wrestling with conflicting definitions of database integrity > as well? You're taking that statement out of its context. I wasn't making a sweeping generalization that database integrity is never a precondition. In the case you were describing above, with the second check, it clearly is. At the time of the statement you quote, we were discussing a program that has to continue to work reliably (i.e. according to some well-defined specification which might include a reduction in functionality) even in the face of no database integrity. In *that case* database integrity isn't a precondition. By definition. HTH, -- Dave Abrahams Boost Consulting www.boost-consulting.com [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Simon Bone on 9 Aug 2005 09:52 On Mon, 08 Aug 2005 15:48:12 -0400, Gerhard Menzl wrote in reply to David Abrahams: > What I am missing particularly is a distinction between "cannot continue > execution of the operation" and "cannot continue execution of the > program". The former does not necessarily mean the latter. My impression > (which may be wrong) is that you restrict the term "precondition > violation" to situations where the latter applies. > > For the former ("cannot continue execution of the operation") an exception is reasonable and can be made safe. For the later ("cannot continue execution of the program") standard C++ exceptions cannot be safely used. I think David's point is that in standard C++ there are many ways to invoke "undefined behaviour" and they all lead to the latter situation. If the inputs to a particular routine can be detected as so screwed up so as to make this inevitable, you need to have some response. That can't be an exception so it has to be something else. Termination is the standard answer, but you might have a platform specific alternative. One thing you can't expect to get away with is trying to combine code to handle both situations. They are fundamentally different in their implications. What you can do is to combine the code that detects each situation, at the start of a routine typically. HTH Simon Bone [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Gerhard Menzl on 10 Aug 2005 14:37 In order to prevent this exchange from breaking into dozens of little sub-arguments, I will try to focus on what I think is the key point (or the key misunderstanding): > I think I have an inkling what you're thinking about this. It's > something like "Ah, but if I throw an exception, the function never > executes, so I can avoid the undefined behavior by not proceeding with > the function and instead initiating stack unwinding." Right? > > That would be wrong. First, the exception throwing is part of what > the function does. If the function's documentation describes a > precondition, the exception happens after entering the function, > i.e. after you've proceeded to execute the "section of code" to which > the precondition applies. So the exception throwing is just part of > the function's undefined behavior. You can't count on it, and use the > knowledge of the exception to make further guarantees about the > reliable behavior of the program. Your definition allows preconditions to be tested using assertions. But assertions are, after all, part of what the function does. If I applied what you wrote above, then triggering an assertion would be just part of the function's undefined behaviour. But is it? When you specify a precondition for a function, you need a clear idea of what the consequences of a violation are, which part of what the function does is affected, and which part isn't. Otherwise, how would you be able to specify the precondition in the first place? If, for instance, as the implementer of flex_array, you specify the precondition idx < size() for operator[](size_type idx), you know that accessing &*begin() + idx in case the precondition is violated possibly invokes undefined behaviour, but querying the size is okay (if it weren't, you couldn't even test the precondition). Why should throwing an exception immediately after testing the precondition be "part of the function's undefined behavior"? This would only be true if the stack is corrupted, but that's not what the precondition is about. Or would you argue that any precondition violation may be due to stack corruption? > The moment when the function's documentation says it throws an > exception in response to some condition being violated -- so that you > can rely on it throwing and make further deductions about the behavior > of the program afterwards -- that exception throwing becomes part of > the function's defined behavior and thus the condition is not a > precondition, by definition. > >>I find your definition of "precondition" very elusive; maybe that's >>the source of our disagreement. > > It's very simple; it is exactly what you'll find at > http://en.wikipedia.org/wiki/Precondition. If I understand you correctly, a precondition ceases to be a precondition as soon as the function that specifies the precondition detects its violation and throws an exception. I don't see how this follows from "a precondition is a fact that must always be true just prior to the execution of some section of code", though. Anyway, does the same hold for asserts? If it doesn't, and asserting preconditions is allowed, but exceptions are not, how do you draw the line? Is it because exceptions are part of the contract, and assertions are not? By the way, how does Eiffel, in the context of which the terms precondition and contract originated, after all, handle this? -- 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: David Abrahams on 10 Aug 2005 16:57
Gerhard Menzl <gerhard.menzl(a)hotmail.com> writes: > In order to prevent this exchange from breaking into dozens of little > sub-arguments, I will try to focus on what I think is the key point (or > the key misunderstanding): > >> I think I have an inkling what you're thinking about this. It's >> something like "Ah, but if I throw an exception, the function never >> executes, so I can avoid the undefined behavior by not proceeding with >> the function and instead initiating stack unwinding." Right? >> >> That would be wrong. First, the exception throwing is part of what >> the function does. If the function's documentation describes a >> precondition, the exception happens after entering the function, >> i.e. after you've proceeded to execute the "section of code" to which >> the precondition applies. So the exception throwing is just part of >> the function's undefined behavior. You can't count on it, and use the >> knowledge of the exception to make further guarantees about the >> reliable behavior of the program. > > Your definition allows preconditions to be tested using assertions. But > assertions are, after all, part of what the function does. If I applied > what you wrote above, then triggering an assertion would be just part of > the function's undefined behaviour. Yes! > But is it? When you specify a precondition for a function, you need a > clear idea of what the consequences of a violation are, > which part of what the function does is affected, and which part > isn't. Otherwise, how would you be able to specify the precondition > in the first place? Go back to the definition of precondition from Wikipedia: the consequence is undefined behavior. > If, for instance, as the implementer of > flex_array, you specify the precondition > > idx < size() > > for operator[](size_type idx), you know that accessing > > &*begin() + idx > > in case the precondition is violated possibly invokes undefined > behaviour, but querying the size is okay (if it weren't, you couldn't > even test the precondition). Why should throwing an exception > immediately after testing the precondition be "part of the function's > undefined behavior"? This would only be true if the stack is corrupted, > but that's not what the precondition is about. Or would you argue that > any precondition violation may be due to stack corruption? Yes, any precondition violation could be the result of stack corruption. >>>I find your definition of "precondition" very elusive; maybe that's >>>the source of our disagreement. >> >> It's very simple; it is exactly what you'll find at >> http://en.wikipedia.org/wiki/Precondition. > > If I understand you correctly, a precondition ceases to be a > precondition as soon as the function that specifies the precondition > detects its violation and throws an exception. 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. > I don't see how this follows from "a precondition is a fact that > must always be true just prior to the execution of some section of > code", though. It doesn't. > Anyway, does the same hold for asserts? If it doesn't, and asserting > preconditions is allowed, but exceptions are not, You can replace "throw an exception" with "call abort," "assert(0)," or anything else you like in (**) and the statement is the same. > how do you draw the line? Is it because exceptions are part of > the contract, and assertions are not? > By the way, how does Eiffel, in the context of which the terms > precondition and contract originated, after all, handle this? I don't know. -- Dave Abrahams Boost Consulting www.boost-consulting.com [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |