Tuesday, October 31, 2006

Assigned or not Assigned, that is the question...

There's a rather interesting discussion taking place in the news://forums.borland.com/borland.public.delphi.non-technical newsgroups about whether or not the Assigned is better than simply testing for nil.  There's also some discussion in that same thread going on about FreeAndNil, which I can cover in another post.  As for Assigned, I thought I'd shed some light on why it was even introduced.  This is also a bit of a peek under the hood (bonnet for those of you across the pond) into some inner workings of the Delphi (BDS) IDE VCL designer.

'if PointerVar <> nil then' has been used to check if a pointer variable holds a useful value (assuming, of course it had been initialized to nil previously).  This statement generates reasonably good machine code, is clear, and serves the intended purpose just fine.  So why add "Assigned"?  Starting with Delphi 1, we introduced to the language the notion of a "method pointer" or some call it a "closure" (although it is not *quite* what a "closure" in the pure sense actually is).  A method pointer is simply a run-time binding consisting of a method and, this is very key, a specific instance of an object.  It is interesting to note that the actual type of the object instance is arbitrary.  The only type checking that needs to take place is to make sure the method signature matches that of the method pointer type.  This is how Delphi achieves its delegation model.

OK, back to Assigned.  The implementation of a method pointer for native code (Win16 and Win32, and the upcomming Win64 implementation), consists of two pointers.  One points to the address of the method and the other to an object instance.  It turns out that the simple if methodpointer <> nil then statement would check that both pointers were nil, which seems logical, right?  And it is.  However, there is a bit of a hitch here.  At design-time, the native VCL form designer pulls a few tricks out of its hat.  We needed a way to somehow assign a value to a component's method pointer instance, but also make sure the component didn't actually think anything was assigned to it and promptly try and call the method (BOOM!!).  Enter, Assigned.  By adding the standard function, Assigned, we could preserve the <> nil sematics, and also introduce a twist.  It turns out, and for the adventurous among you can verify this, that Assigned only checks one of the two pointers in a method pointer.  The other pointer is never tested.  So what happens is that when you "assign" a method to the method pointer from within the IDE's Object Inspector, the designer is actually jamming an internally created index into the other (non-Assigned-tested) pointer within the method pointer structure.  So as long as your component uses if Assigned(methodpointer) then before calling through the method pointer, your component code will never misbehave at design-time.  So ever since Delphi 1, we've diligently drilled into everyone's head, "Use Assigned... Use Assigned... Use Assigned...."  It think it worked.  For component writers it is critical that Assigned be used when testing the value of published method pointers before calling through them.  For all other cases, it is a little muddier and less defined.  Personally, I still use the <> nil pattern all the time.  Maybe it's because I'm "old skool", I don't know... I do, however, always use Assigned for testing method pointers, for the reasons given above.

I've seen some rather strange arguments as to why you should always use Assigned, even on normal instance references and pointers.  One, that I found kinda strange was that "in the future, 'nil' may be defined as something other than a pointer value with all zeros (0)"  If that were going to be the case, don't you think that the "value" of nil will also change to reflect that condition?  Another factor is, why on earth would you ever need to use a non-zero valued nil?  That, to me, should be one of those immutable laws of computing, like PChars (char*) are always terminated with a zero (0), nil should always be a zero (0) value.  Now, I do realized that the Pascal definition of 'nil' doesn't necessarily mean zero (0), but it has been so convienient and consistent, that for any new platform or architecture that would want to change it would need to have an extremenly compelling reason to redefine it.  At some point, I'll set the WABAC machine and we can all go back to investigate some other obscure factoid and try to dig up the the reasoning behind certain decisions and designs related to Delphi, VCL, and the IDE.  For now, keep on using "Assigned(someinstance)" or "<> nil" with impunity... however for method pointers, it's if Assigned(methodpointer) then all the way!