From: Gerhard Menzl on
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
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
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
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
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! ]

First  |  Prev  |  Next  |  Last
Pages: 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Next: C++/CLI limitations?