Don:
>> 2) I have found many situations where I am able to solve a problem with both
>> a simple and slow brute force solver, and a complex and fast algorithm to
>> solve a problem. The little program maybe is too much slow for normal usage,
>> but it's just few lines long (especially if I use lot of std.algorithm
>> stuff) but it's much less likely to contain bugs.
> Sorry, but personally I don't believe that this is useful outside of toy
> examples.
> The question is, what bugs does it find that aren't found by a trivial unit
> test?
> There are two cases:
> (1) it's a very tight test. In which case, it's essentially a unit test.
> or (2) it's a very loose test. In which case, it doesn't find bugs.
Putting a simpler algorithm in the post-condition implements a third
possibility you are missing.
Usually unit tests verify some specific cases (you are also able to add generic
testing code in the unit test, but this is just like moving the postcondition
elsewhere).
If you put an alternative algorithm in the postcondition (under debug{} if you
want), you have some advantages:
- It's tight, because the second algorithm is supposed to always give the same
results as the function.
- It works with the real examples the program is run too, not just the cases
you have put in the unit test. Sometimes you forget to add certain cases in the
unittests. Putting the test in the postcondition makes sure it always run, for
all the inputs your function is run on (unless you disable it), so you will
catch the cases you didn't think of in the unittests.
> The crucial feature is, they do NOTHING except find bugs in the function
> they are attached to.
In Eiffel you have the prestate too (the old), so the postcondition is the only
place where such information is usable. I hope prestate will be added to D DbC,
because it's a majob sub-feature of DbC. But I don't agree that postconditions
are useless in D.
> For starters, it really needs to be a function with multiple return
> values. Otherwise, you can just stick asserts just before your return
> statement, and you don't need __old or any such thing.
If a function has multiple return values the out(result) helps make sure all
the return paths are verified.
If the function has only one return value it helps anyway, because it helps you
not forget to verify the result.
> Under what circumstances are they are more valuable than any other assert
> inside a function?
I have already given some answers. Another answer is this:
int foo(int x)
in {
// ...
}
out(result) {
auto y = computeSomething(result);
assert(y ...);
assert(y ...);
}
body {
// ...
}
The out{} helps you organize your code, separating the tests of the body from
the postcondition tests. Also in the postcondition you are allowed to define
new variables and call things. All this out(){} code vanishes in release mode.
Ho do you do that with just asserts inside the body?
If you do this the asserts will vanish in release mode, but the y will be
computed still, wasting computations (a smart compiler is able to see y is not
used and etc, but it's not sure this optimization happens if the computation of
y is complex and it's done in-place):
int foo(int x)
in {
// ...
}
body {
result = ...;
auto y = computeSomething(result);
assert(y ...);
assert(y ...);
return result;
}
I presume there are ways to disable the computation of y in release mode, but I
don't want to think about them. I just stick the y computation in the
postcondition and the compiler will take care of it.
Bye,
bearophile