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!

10 comments:

  1. I prefer the readability of the Assigned(pointer) vs pointer<>Nil


    I find that rather than using 2 different methods to check pointers, it is easier to just use the one. My fingers type it automatically without my even having to think about it, and that is reason enough right there.


    But then, I have developed a LOT of components.


    It is funny, but I don't remember having Assigned drilled into my head. I use it out of habit and prefernce more than anything. I'll have to drag out the D1 component writer's manual and take a trip down memory lane.


    That said, every once in a while, I get lazy and <>nil pops out. I go back and change it pretty quick, because it doesn't meet my personal "clean code" standard.

    ReplyDelete
  2. Assigned() vs. <> nil on normal pointers is all about preference. We *could* have made Assigned only work with method pointers. However, as you pointed out, it keeps things consistent and easy to read.

    ReplyDelete
  3. Thanks for pointing this out, Allen!

    I prefer testing for nil on normal pointers because I generally do have to initialize them to nil. It's less readable/consistent to produce and test situation with different incantations:


    p := nil; // initialize

    ...

    if Assigned(p) then // test p<>nil

    ...

    FreeAndNil(p); // yet another way to associate p and nil


    For method pointers I've always used Assigned(), and probably only because everyone else does. But then again, method pointers aren't really "dynamically allocated memory", they're just address references, so their management falls under different coding patterns than objects and other heap-eating allocations.

    ReplyDelete
  4. >Personally, I still use the <> nil pattern all the time. [...] I do, however, always use Assigned for testing method pointers, for the reasons given above.


    Actually, you can't get it wrong (e.g.):


    if FOnClick <> nil ...


    isn't valid syntax.

    ReplyDelete
  5. Actually, Per, you *can* get it wrong. Your example "if FOnClick <> nil" isn't valid syntax because the compiler sees the FOnClick identifier and assumes you are trying to *call* through the method pointer.


    The correct syntax is:


    if @FOnClick <> nil then.....


    This syntax will work but (as pointed out by Allen) it will test both words of the pointer, which is a no-no for design-time components. However, the Assigned (FOnClick) syntax will operate correctly in these circumstances.


    To look at things at the machine-code level:


    if @FOnClick <> nil produces

    CMP DWORD PTR [Self+XXXX], 0


    if Assigned (FOnClick) produces

    CMP WORD PTR [Self+XXXX], 0


    Dave

    ReplyDelete
  6. Allen, don't ever leave Borland! Unless it's to go to a spin-off company. Can't be too many people that know as much about the inner workings of the compiler, and no WABAC machine is available (to the public anyway...)

    ReplyDelete
  7. Thanks for that information. I always wondered what the assigned was about.



    ReplyDelete
  8. I don't understand the argument here. In general there is a difference between assigning NIL to a variable and NOT assigning ANYTHING to a variable. Some languages care about that.


    It's my understanding that in the case of Delphi, the "assigned" function is needed simply to prevent an event callback from being triggered when you're only trying to see if there IS an event callback.


    Therefore, think of that function "assigned" as if it were called something like "eventdefined" and then there's no issue.


    if EventDefined(OnClick)

    then .....


    ReplyDelete
  9. I did some investigation into how nil references can cause AV's or Invalid Pointer References. What I found was interesting:


    http://blogs.slcdug.org/jjacobson/archive/2006/10/27/4434.aspx

    ReplyDelete
  10. Encounter that to work with Assigned is to work upper in level, which will assure a greater compatibility at some future date to me (probably in the future not very distant)

    ReplyDelete

Please keep your comments related to the post on which you are commenting. No spam, personal attacks, or general nastiness. I will be watching and will delete comments I find irrelevant, offensive and unnecessary.