digitalmars.D - A temporary breach of the class invariant
- Arcane Jill (21/23) Jun 10 2004 and that compiled ok and runs fine. Let's say, for sake of argument, tha...
- Mike Swieton (15/22) Jun 10 2004 I'll second this. This would be tremendously useful. One specific instan...
- Walter (18/41) Jun 10 2004 Invariants only get called on entry/exit on *public* functions. Public
- Arcane Jill (25/37) Jun 11 2004 That isn't relevant. Getter and setter functions allow you to change the
- Daniel Horn (17/77) Jun 11 2004 This is the reason that I never put invariants or inout checks in my cod...
- J Anderson (7/11) Jun 11 2004 I found this ability of D quite useful when I needed to remove a public
- Oskar Linde (23/26) Jun 22 2004 Is this true for the current dmd compiler? The following piece of code
- Ant (3/7) Jun 22 2004 but that's not always desirable...
- Oskar Linde (12/26) Jun 22 2004 You can always do:
- Ant (3/29) Jun 22 2004 it's a bit artificial, isn't it?...
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
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
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 TOOstrict.The problem comes with inlining stuff. Say you had:thea = 1; b = 2;and that compiled ok and runs fine. Let's say, for sake of argument, thatclass invariant does not hold between the two lines. Now the problem comes when you replace these two lines with hopefullyinlinefunctions. Now the thing about inline functions is - even though they'reinline,they're still FUNCTIONS, which means that the class invariant gets tested.Infact, if you use any "getter" functions within the invariant test itself,you'vegot an infinite loop and a stack overflow. I would like to propose that functions be allowed temporarily to suspendtheclass invariant. Currently, the compiler checks it on entry and exit toeveryfunction. I would argue that it should only be checked whenever a functioniscalled /from outside the class/. Otherwise a simple getter method cantotallyfoil the most audacious design by contract. (And in D, getter functionsincludeproperties). 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 stopsother DbCfrom working, things have gone too far. So I would like to add my supporttosome relaxation in this area. Arcane Jill
Jun 10 2004
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
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
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
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
In article <cba6h3$ddj$1 digitaldaemon.com>, Oskar Linde says...Walter wrote:but that's not always desirable... AntInvariants 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.
Jun 22 2004
Ant wrote:In article <cba6h3$ddj$1 digitaldaemon.com>, Oskar Linde says...You can always do: class C { public: int get() { return privateGet(); } int temporaryInvariantBreach() { /* use privateGet instead of get()*/ } private: int privateGet() {...} } /OskarWalter wrote:but that's not always desirable... AntInvariants 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.
Jun 22 2004
In article <cba92q$hdj$1 digitaldaemon.com>, Oskar Linde says...Ant wrote:it's a bit artificial, isn't it?... AntIn article <cba6h3$ddj$1 digitaldaemon.com>, Oskar Linde says...You can always do: class C { public: int get() { return privateGet(); } int temporaryInvariantBreach() { /* use privateGet instead of get()*/ } private: int privateGet() {...} } /OskarWalter wrote:but that's not always desirable... AntInvariants 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.
Jun 22 2004