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