Invariants are useless the way they are defined

deadalnix deadalnix at gmail.com
Sun Aug 25 09:47:57 PDT 2013


On Sunday, 25 August 2013 at 16:22:01 UTC, Andrei Alexandrescu 
wrote:
> On 8/25/13 5:16 AM, deadalnix wrote:
>> As they are defined now, invariant are plain useless. I find 
>> myself
>> disabling them one by one in my code as soon as cases get 
>> outside simple
>> trivia.
>>
>> The problem is that invariant are checked at the beginning/end 
>> on public
>> function calls. As a consequence, it is impossible to use any 
>> public
>> method in an invariant.
>
> But this is what they are for - check the state of an object 
> upon access through the client-accessible interface. Simple 
> code factorization (use a private method forwarded to by the 
> public one) takes care of this situation.
>

Yes that can be worked around, but the code ends up more and more 
complex, with many function forwarding to one another.

This can become very messy with class hierarchies.

>> For instance, just right now I did refactor a struct to use 
>> bitfields,
>> in order to make it more compact. Now I have to remove the 
>> invariant as
>> field access are now function calls.
>
> I don't get this case. Code?
>

struct Identifiable {
	union {
		Symbol sym;
		Expression expr;
		Type type;
	}
	
	import d.ast.base : TypeQualifier;
	
	import std.bitmanip;
	mixin(bitfields!(
		Tag, "tag", 2,
		TypeQualifier, "qual", 3,
		uint, "", 3,
	));
	
	@disable this();
	
	// For type inference.
	this(typeof(null));
	
	this(Identifiable i) {
		this = i;
	}
	
	this(Symbol s) {
		tag = Tag.Symbol;
		sym = s;
	}
	
	this(Expression e) {
		tag = Tag.Expression;
		expr = e;
	}
	
	this(QualType qt) {
		tag = Tag.Type;
		qual = qt.qualifier;
		type = qt.type;
	}
}

I used to have an invariant that check the sanity of the tagged 
union, but now I have to double the interface to the bitfield.

This one is still fixable by doubling the interface, but the same 
thing with the bitfield in a super class from another module and 
you are doomed.

>> Another typical situation is when you want to assert certain 
>> properties
>> in a class hierarchy, where calling virtual method is part of 
>> the
>> invariant check.
>>
>> invariant should (and must to be useful) be inserted at callee 
>> point,
>> when the callee isn't itself subject to invariant insertion, 
>> and around
>> public memeber manipulation (when the manipulator isn't 
>> subject to
>> invariant insertion).
>>
>> Any other pattern it doomed to create infinite recursion for 
>> non trivial
>> invariant checks.
>
> There might be something here, but more like a performance 
> problem. I do see how calls of public methods from an invariant 
> could cause trouble, but I'd say that's a problem with the 
> invariant design.
>
>
> Andrei

This is a performance issue as well. But mostly it makes 
invariant useless in many cases when class hierarchies are in 
place and probably the worst part is that invariant are not even 
ensured as they do not trigger when writing to public fields (as 
triggered by the callee, and we have no callee in this case).


More information about the Digitalmars-d mailing list