assert(obj) is an atrocity
    Alex Rønne Petersen 
    xtzgzorex at gmail.com
       
    Sat Nov 19 10:02:13 PST 2011
    
    
  
On 13-11-2011 22:38, Jonathan M Davis wrote:
> On Sunday, November 13, 2011 18:52:35 Alex Rønne Petersen wrote:
>> I'd just like to add, regarding asserting invariants: Why not just *make
>> invariants callable*? I.e. obj.invariant() invokes the invariant
>> function. invariant is already a keyword, and invariants are declared
>> like functions, so this seems like a logical thing to do.
>
> Call __invariant. But like all __ functions, I don't think that it's really
> intended to be called directly in code. You can do it though.
>
> Really, I don't think that there's a problem with assert(obj) calling obj's
> invariant. It's just extra overhead in a non-release build which is running a
> set of assertions which should be true anyway (assuming that it's being called
> outside of the class). It's the fact that it doesn't check for null first which
> is so atrocious, since that _completely_ changes the semantics of what obj
> normally does when implicitly converted to bool. If it checked for null first,
> then the normal semantics hold with the addition of checking the invariant.
I ran into a funny issue today. I wanted to assert that all items in a 
collection are not null. Naturally, I just did something like:
invariant()
{
     foreach (item; _store)
         assert(item);
}
That was a bad idea, however! Because this assert calls the invariant of 
item, *it can end up causing an infinite loop*. This can happen in a 
scenario such as:
class A
{
     Collection!B col;
     invariant()
     {
         assert(col);
     }
}
class B
{
     A a;
     invariant()
     {
         assert(a);
     }
}
auto a = new A(); a.col = new Collection!B();
auto b = new B(); b.a = a; a.col.add(b);
assert(b);
The call sequence will now look something like:
1: B.invariant()
2: A.invariant()
3: Collection!B.invariant()
4: *B.invariant()*
5: ... and so on ...
I just thought it was worth mentioning how calling the invariant in 
assert can lead to very unexpected results. Of course, the solution is 
simple: assert(item !is null); in the collection invariant. Still, I 
think this is just as unintuitive as the missing null check.
I think we need to keep it simple here.
>
> The fun part about assert(obj) though is that it doesn't work with structs -
> not directly anyway. If obj is a struct, assert(obj) tries to convert it to
> bool like it would do normally. However, assert(&obj) _does_ call the
> invariant. Pointers to structs are treated exactly the same as references to
> classes in this case (complete with the lack of a null check). So, it's
> arguably consistent, but it's a bit weird.
>
> In any case, what really needs to be done is to add the extra null check. It
> won't silently break code to do that, and it'll avoid the surprise and
> confusiong of assert(obj) not checking for null and blowing up as a result.
>
> - Jonathan M Davis
- Alex
    
    
More information about the Digitalmars-d
mailing list