www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - A temporary breach of the class invariant

reply Arcane Jill <Arcane_member pathlink.com> writes:
I had to take the class invariants out of Int, I'm afraid. D is just TOO strict.

The problem comes with inlining stuff. Say you had:

    a = 1;
    b = 2;
and that compiled ok and runs fine. Let's say, for sake of argument, that the class invariant does not hold between the two lines. Now the problem comes when you replace these two lines with hopefully inline functions. Now the thing about inline functions is - even though they're inline, they're still FUNCTIONS, which means that the class invariant gets tested. In fact, if you use any "getter" functions within the invariant test itself, you've got an infinite loop and a stack overflow. I would like to propose that functions be allowed temporarily to suspend the class invariant. Currently, the compiler checks it on entry and exit to every function. I would argue that it should only be checked whenever a function is called /from outside the class/. Otherwise a simple getter method can totally foil the most audacious design by contract. (And in D, getter functions include properties). This is similar to the encoder/decoder calling each other problem. There are a number of ways to fix this, including simply looking out for recursion during all DbC, and refusing to recurse. But when DbC stops other DbC from working, things have gone too far. So I would like to add my support to some relaxation in this area. Arcane Jill
Jun 10 2004
next sibling parent Mike Swieton <mike swieton.net> writes:
On Thu, 10 Jun 2004 21:48:52 +0000, Arcane Jill wrote:
 I would like to propose that functions be allowed temporarily to suspend the
 class invariant. Currently, the compiler checks it on entry and exit to every
 function. I would argue that it should only be checked whenever a function is
 called /from outside the class/. Otherwise a simple getter method can totally
 foil the most audacious design by contract. (And in D, getter functions include
 properties).
 
I'll second this. This would be tremendously useful. One specific instance in which it would be handy is enforcing that mixins are initialized properly, by allowing an init method to be called outside of the invariant, and having the invariant check that it was called. A temporary solution is to have the 'real' methods be implemented on a nested class, and have the invariants (located in the outer class) assert things about the nested class. The nested class's calls would be direct. This would solve your problem in an extremely ugly and extra-work-ish manner, but it would work ;) Mike Swieton __ One of the main causes of the fall of the Roman Empire was that, lacking zero, they had no way to indicate successful termination of their C programs. - Robert Firth
Jun 10 2004
prev sibling parent reply "Walter" <newshound digitalmars.com> writes:
Invariants only get called on entry/exit on *public* functions. Public
functions shouldn't need to access getter/setter functions, they can operate
directly on the private data.

"Arcane Jill" <Arcane_member pathlink.com> wrote in message
news:caal04$1v0d$1 digitaldaemon.com...
 I had to take the class invariants out of Int, I'm afraid. D is just TOO
strict.
 The problem comes with inlining stuff. Say you had:

    a = 1;
    b = 2;
and that compiled ok and runs fine. Let's say, for sake of argument, that
the
 class invariant does not hold between the two lines.

 Now the problem comes when you replace these two lines with hopefully
inline
 functions. Now the thing about inline functions is - even though they're
inline,
 they're still FUNCTIONS, which means that the class invariant gets tested.
In
 fact, if you use any "getter" functions within the invariant test itself,
you've
 got an infinite loop and a stack overflow.

 I would like to propose that functions be allowed temporarily to suspend
the
 class invariant. Currently, the compiler checks it on entry and exit to
every
 function. I would argue that it should only be checked whenever a function
is
 called /from outside the class/. Otherwise a simple getter method can
totally
 foil the most audacious design by contract. (And in D, getter functions
include
 properties).

 This is similar to the encoder/decoder calling each other problem.

 There are a number of ways to fix this, including simply looking out for
 recursion during all DbC, and refusing to recurse. But when DbC stops
other DbC
 from working, things have gone too far. So I would like to add my support
to
 some relaxation in this area.

 Arcane Jill
Jun 10 2004
next sibling parent reply Arcane Jill <Arcane_member pathlink.com> writes:
In article <cabiqn$9a9$1 digitaldaemon.com>, Walter says...
Invariants only get called on entry/exit on *public* functions. Public
functions shouldn't need to access getter/setter functions, they can operate
directly on the private data.
That isn't relevant. Getter and setter functions allow you to change the implementation of the property without having to go through and recode your whole source file. They enable you to have a single point of failure, in the event of a bug. They allow your code to be more maintainable by allowing you avoid massive duplication of code. And when your getter and setter functions have if/else logic inside them; access union elements depending on what's in the union; and so on, you DON'T want to be duplicating all that code all over the place. Regardless of whether they are inlined, I still want to be able to use them. I see three possible solutions, in terms of changing the language, and at least one workaround if those won't happen. (a) Introduce an attribute keyword which marks a function as being callable without invoking the class invariant test on its entry and exit; (b) Introduce a statement modifier keyword which indicates that within a statement or statement block, the class invariant will not be tested no matter what functions are called (c) Never test the class invariant if the callee is a class member variable. My favorite is (c). The workaround is ugly, but it does work. What you do is you replace your getter functions as follows. Before:
    class A
    {
        T getValue() { /* whatever */
    }
After:
    class A
    {
        T getValue() { invariantSafeGetValue(this); }
    }

    package T invariantSafeGetValue(A a) { /* whatever */ }
then replace all calls to getValue() with invariantSafeGetValue(this); ..but obviously I'd rather you went for plan (c). Jill
Jun 11 2004
parent Daniel Horn <hellcatv hotmail.com> writes:
This is the reason that I never put invariants or inout checks in my code.
In this case the testing solution causes most of the bugs (i.e. endless 
recursion in an unforseen function call order)
I mean you could have the unit tests muck up something at the last 
iteration of a loop when you finally visit that function that calls the 
recursive unit test...and then you lose your days worth of results...

so ya until I have to stop worrying about runtime endless loops 
occuring, these tests IMHO cause more problems than they solve.

c) is fine, but what if you test another class's result which tests your 
result...it pushes the error down one level--which may be enough for 
practical purposes...but it doesn't solve the main problem...which is 
recursion. I Think the compiler should detect recursion in these unit 
tests (it could do it at runtime by saving a hashtable of previously 
called functions... and then if it sees itself just abort with 
"everything is fine")
-Daniel
Arcane Jill wrote:
 In article <cabiqn$9a9$1 digitaldaemon.com>, Walter says...
 
Invariants only get called on entry/exit on *public* functions. Public
functions shouldn't need to access getter/setter functions, they can operate
directly on the private data.
That isn't relevant. Getter and setter functions allow you to change the implementation of the property without having to go through and recode your whole source file. They enable you to have a single point of failure, in the event of a bug. They allow your code to be more maintainable by allowing you avoid massive duplication of code. And when your getter and setter functions have if/else logic inside them; access union elements depending on what's in the union; and so on, you DON'T want to be duplicating all that code all over the place. Regardless of whether they are inlined, I still want to be able to use them. I see three possible solutions, in terms of changing the language, and at least one workaround if those won't happen. (a) Introduce an attribute keyword which marks a function as being callable without invoking the class invariant test on its entry and exit; (b) Introduce a statement modifier keyword which indicates that within a statement or statement block, the class invariant will not be tested no matter what functions are called (c) Never test the class invariant if the callee is a class member variable. My favorite is (c). The workaround is ugly, but it does work. What you do is you replace your getter functions as follows. Before:
   class A
   {
       T getValue() { /* whatever */
   }
After:
   class A
   {
       T getValue() { invariantSafeGetValue(this); }
   }

   package T invariantSafeGetValue(A a) { /* whatever */ }
then replace all calls to getValue() with invariantSafeGetValue(this); ..but obviously I'd rather you went for plan (c). Jill
Jun 11 2004
prev sibling next sibling parent J Anderson <REMOVEanderson badmama.com.au> writes:
Walter wrote:

Invariants only get called on entry/exit on *public* functions. Public
functions shouldn't need to access getter/setter functions, they can operate
directly on the private data.

  
I found this ability of D quite useful when I needed to remove a public member variable from a class. That way I did not need to go find all the locations of that variable (in itself and inherited classes) and replace it with its new location. -- -Anderson: http://badmama.com.au/~anderson/
Jun 11 2004
prev sibling parent reply Oskar Linde <d98-oliRE.MO.VE nada.kth.se> writes:
Walter wrote:
 Invariants only get called on entry/exit on *public* functions. Public
 functions shouldn't need to access getter/setter functions, they can operate
 directly on the private data.
Is this true for the current dmd compiler? The following piece of code gives an AssertError: <code> class Test { this() { a = 0; b = 0; } void test() { incA(); decB(); } private: int a,b; void incA() { a++; } void decB() { b--; } invariant { assert(a+b == 0); } } void main() { Test t = new Test(); t.test(); } </code> If invariants are only tested at entry and exit of public functions, there should be no assertion in test() between the private calls? /Oskar
Jun 22 2004
parent reply Ant <Ant_member pathlink.com> writes:
In article <cba6h3$ddj$1 digitaldaemon.com>, Oskar Linde says...
Walter wrote:
 Invariants only get called on entry/exit on *public* functions. Public
 functions shouldn't need to access getter/setter functions, they can operate
 directly on the private data.
but that's not always desirable... Ant
Jun 22 2004
parent reply Oskar Linde <d98-oliRE.MO.VE nada.kth.se> writes:
Ant wrote:

 In article <cba6h3$ddj$1 digitaldaemon.com>, Oskar Linde says...
 
Walter wrote:

Invariants only get called on entry/exit on *public* functions. Public
functions shouldn't need to access getter/setter functions, they can operate
directly on the private data.
but that's not always desirable... Ant
You can always do: class C { public: int get() { return privateGet(); } int temporaryInvariantBreach() { /* use privateGet instead of get()*/ } private: int privateGet() {...} } /Oskar
Jun 22 2004
parent Ant <Ant_member pathlink.com> writes:
In article <cba92q$hdj$1 digitaldaemon.com>, Oskar Linde says...
Ant wrote:

 In article <cba6h3$ddj$1 digitaldaemon.com>, Oskar Linde says...
 
Walter wrote:

Invariants only get called on entry/exit on *public* functions. Public
functions shouldn't need to access getter/setter functions, they can operate
directly on the private data.
but that's not always desirable... Ant
You can always do: class C { public: int get() { return privateGet(); } int temporaryInvariantBreach() { /* use privateGet instead of get()*/ } private: int privateGet() {...} } /Oskar
it's a bit artificial, isn't it?... Ant
Jun 22 2004