Temporary suspension of disbelief (invariant)
bearophile
bearophileHUGS at lycos.com
Tue Oct 26 17:00:22 PDT 2010
I have not asked this in the D.learn newsgroup because I think it may be a bit interesting for other general people too.
In D contract programming the invariant is not called before/after private methods. I think that in some cases you may want to disable the invariant even in some public methods. If your class/struct has a public method that invalidates the state of the class instance, and one public method that fixes the class instance state (I am thinking about certain data structures where in a first phase you may add many items, and then you ask the data structure to clean up itself and become coherent. This may avoid some computations), I have found this way to implement it:
import std.stdio: writeln;
class Foo {
private int someInstanceState = 1;
// A ghost field, but it does't vanish in release mode
// as it is supposed to do:
// http://d.puremagic.com/issues/show_bug.cgi?id=5027
private int validState_ghostField = 0;
private bool isInvariantEnabled() {
return validState_ghostField >= 0;
}
private void incValidState()
in {
assert(validState_ghostField < validState_ghostField.max);
} body {
validState_ghostField++;
}
private void decValidState()
in {
assert(validState_ghostField > validState_ghostField.min);
} body {
validState_ghostField--;
}
invariant() {
writeln("invariant validState_ghostField: ", validState_ghostField);
if (isInvariantEnabled()) {
writeln("Invariant is enabled");
// tests the coherence of the instance state
assert(someInstanceState == 1);
} else {
writeln("Invariant is disabled");
}
}
public void fixState() {
scope(exit) incValidState(); // scope(exit)?
writeln("fixState validState_ghostField: ", validState_ghostField);
someInstanceState = 1;
}
public void invalidateState() {
scope(exit) decValidState(); // scope(exit)?
writeln("invalidateState validState_ghostField: ", validState_ghostField);
someInstanceState = 0;
}
}
void main() {
auto f = new Foo;
writeln("State 1: ", f.someInstanceState);
writeln("isInvariantEnabled: ", f.isInvariantEnabled());
f.invalidateState();
writeln("State 2: ", f.someInstanceState);
writeln("isInvariantEnabled: ", f.isInvariantEnabled());
f.fixState();
writeln("State 3: ", f.someInstanceState);
writeln("isInvariantEnabled: ", f.isInvariantEnabled());
}
(Like the GC disable/enable validState_ghostField goes up and down, and allows nesting too.)
All this looks bug-prone, and surely hairy, but it looks potentially useful. Is it a good idea to design a class that uses such temporary suspension of the invariant?
Bye,
bearophile
More information about the Digitalmars-d
mailing list