Delphi 2009 : TQueue<T>.Dequeue vs TObjectQueue<T>.Dequeue

I was poking around inside Generics.Collections the other day while writing some code for a future post on pooling, and I came up against something puzzling.

Generics.Collections has a class called TQueue<T>, which is a generic version of the classic first-in-first-out datastructure. The nice thing of course is that it can now be made type-safe by specifying a type parameter. If you’ve ever looked at a queue, such as the non-generic one that ships with Delphi already, or even thought about a queue datastructure, looking at TQueue<T> will not hold many surprises.

Generics.Collections also has a class called TObjectQueue<T>, which descends from TQueue<T> and adds object ownership (ie. the queue will destroy any objects it holds when you destroy it). This is implemented using an interesting looking notification process that probably deserves investigation in a future post, but again, the object ownership concept should be one many Delphi developers would be familiar with from TObjectList, etc, in previous versions of Delphi.

TQueue<T> has a function called Dequeue which returns the front item off the queue. The signature looks like this:

function TQueue.Dequeue: T;

However, when I came to use TObjectQueue<T> its Dequeue method looks like this:

 

procedure TObjectQueue.Dequeue;

That’s right, a procedure. Internally it still takes the front item off the queue, in fact all it does is call the inherited TQueue<T>.Dequeue, but it doesn’t give it back to you.

I have to admit, my initial reaction to this was to assume I’d navigated to the wrong class so I went back to the starting point and walked my way through it again. Nope, still a procedure. No matter how I navigated to it, it wouldn’t turn into a function.

At this point I took the dogs for a walk and thought about it some more. When I got back, unfortunately, it was still a procedure. Further, after working through the notification code a little, it turns out that not only does TObjectQueue<T>.Dequeue take the front item off the queue and not give it back to you, it also Frees the item.

Now, I’d love to tell you that after careful examination of the rest of the code I discovered what was going on but that would be complete bollocks. Instead, I dashed off an email to Barry Kelly.

The answer comes down to the fact that because TObjectQueue<T> owns the objects, you need the ability to do two different things:

  • Remove the front item from the queue and have it automatically destroyed by its owner, the queue. Maybe this is after you’ve looked at it using Peek and decided you don’t want it. This is what TObjectQueue<T>.Dequeue is for.
  • Remove the front item from the queue, and have it returned to you. Part of this is that it will be removed from the queue’s ownership and it becomes your responsibility to free. This is what the method Extract is for, which to be honest I didn’t notice was there, and is in fact defined up on the TQueue<T> as well.

If you think about the names Dequeue vs Extract, this kind of makes sense. Some small part of me still finds it a little disconcerting that a function turns into a procedure between TQueue<T> and TObjectQueue<T>, but I’m mostly over it by now. Chances are if I’d stumbled upon Extract before Dequeue, none of this would have occurred. Anyway, I changed my pool code to use Extract instead of Dequeue, all my unit tests passed and I was free to go watch the Brisbane Broncos get robbed of a spot in the Rugby League finals.

Anyway, I hope this will help some future poor soul googling away to try and find out why Dequeue changes from a function to a procedure.

Anonymous Methods, Generics and Enumerators

I’ve been playing around with Anonymous Methods in Delphi 2009 a little bit lately, and I thought one of my experiments might be worth sharing.

I decided I would try to extend TList<T> so that when you enumerate over it in a for..in loop, not every item would be returned. Specifically, only items that passed a filter criteria would be returned, and I would use an anonymous method to specify the filter criteria.

This actually turned out to be kind of fun, as I got to play with Enumerators, Generics and Anonymous Methods all in one fairly short piece of code.

So, first I looked at TList<T>. I was hoping there was some way I could specify that it should use a different Enumerator than the default, but unfortunately I could not find a way. So I descended from TList<T> to create a TFilteredList<T> to:

  • add a couple of methods, ClearFilter and SetFilter, which I’ll come back to later
  • define TFilteredEnumerator<T>
  • reintroduce the GetEnumerator method to create an instance of TFilteredEnumerator<T> instead of TList<T>’s standard TEnumerator<T>

I also declared an anonymous method type called TFilterFunction<T> which takes a parameter of type T and returns true or false depending on whether the parameter passed or failed the filter criteria respectively.

Here’s the definition:

  TFilterFunction = reference to function(Item : T) : boolean;
  TFilteredList = class(TList)
  private
    FFilterFunction: TFilterFunction;
  public
  type
      TFilteredEnumerator = class(TEnumerator)
      private
        FList: TFilteredList;
        FIndex: Integer;
        FFilterFunction : TFilterFunction;
        function GetCurrent: T;
      protected
        function DoGetCurrent: T; override;
        function DoMoveNext: Boolean; override;
        function IsLast : Boolean;
        function IsEOL : Boolean;
        function ShouldIncludeItem : Boolean;
      public
        constructor Create(AList: TFilteredList;
                           AFilterFunction : TFilterFunction);
        property Current: T read GetCurrent;
        function MoveNext: Boolean;
      end;
    function GetEnumerator: TFilteredEnumerator; reintroduce;
    procedure SetFilter(AFilterFunction : TFilterFunction);
    procedure ClearFilter;
  end;

Most of the methods on TFilteredEnumerator<T> are not terribly interesting. The constructor takes a reference to an instance of our TFilterFunction<T> anonymous method, which it stores in the FFilterFunction field. This constructor gets called from the aforementioned GetEnumerator method of TFilteredList<T>.

Most of the hard work is done by the MoveNext method, which looks like this:

function TFilteredList.TFilteredEnumerator.MoveNext: Boolean;
begin
  if IsLast then
    Exit(False);

  repeat
    Inc(FIndex);
  until isEOL or ShouldIncludeItem;

  Result := not IsEol;
end;

It is invoked when the Enumerator wants to move to the next item in the List, returning True if it does this successfully, otherwise returning False.  In this case, it:

  • first checks to see if we’re already at the end of the List, in which case it bails out returning False, we’re at the end of the list.
  • otherwise, it keeps incrementing the position until either we’re passed the last item or we hit an item that passes our filter criteria.

ShouldIncludeItem invokes our FIlterMethod if one is defined, passing in the current item in the list. It looks like:

function TFilteredList.TFilteredEnumerator.ShouldIncludeItem: Boolean;
begin
  Result := True;
  if Assigned(FFilterFunction) then
    Result := FFilterFunction(FList[FIndex]);
end;

The following code creates a TList<TPerson> and loads it up, then enumerates over each TPerson in the list that is older than 18. You can see the call to SetFilter where we specify the anonymous method that will do the actual filtering. It then clears the filter and enumerates over all the TPerson objects.

 

    PersonList := TFilteredList.Create;
    try
      PersonList.Add(TPerson.Create('Fred Adult', 37));
      PersonList.Add(TPerson.Create('Julie Child', 15));
      PersonList.Add(TPerson.Create('Mary Adult', 18));
      PersonList.Add(TPerson.Create('James Child', 12));

      writeln('--------------- Filtered Person List --------------- ');
      PersonList.SetFilter(function(Item : TPerson): boolean
                           begin
                             Result := Item.Age >= 18;
                           end);

      for P in PersonList do
      begin
        writeln(P.ToString);
      end;

      writeln('--------------- Unfiltered Person List --------------- ');
      PersonList.ClearFilter;
      for P in PersonList do
      begin
        writeln(P.ToString);
      end;

    finally
      PersonList.Free;
    end;

This produces the following output:

————— Filtered Person List —————

Name = Fred Adult, Age = 37

Name = Mary Adult, Age = 18

————— Unfiltered Person List ————-

Name = Fred Adult, Age = 37

Name = Julie Child, Age = 15

Name = Mary Adult, Age = 18

Name = James Child, Age = 12

The generics come into play so that I can use the same list for something other than TPerson objects, in the example below, Integers:

    IntegerList := TFilteredList.Create;
    try
      IntegerList.Add(1);
      IntegerList.Add(2);
      IntegerList.Add(3);
      IntegerList.Add(4);

      writeln('--------------- Filtered Integer List --------------- ');
      IntegerList.SetFilter(function(Item : Integer): boolean
                            begin
                               Result := Item >= 3;
                            end);

      for I in IntegerList do
      begin
        writeln(IntToStr(I));
      end;

      writeln('--------------- Unfiltered Integer List --------------- ');
      IntegerList.ClearFilter;

      for I in IntegerList do
      begin
        writeln(IntToStr(I));
      end;

    finally
      IntegerList.Free;
    end;

Now, I’m not sure that I really want to write my code like this. I like the fact that there’s a nice separation between the code that decides which items to act on, and the code that actually acts on them, rather than having them all jumbled together inside the for..in loop.

Ironically, that’s also the bit that I don’t like, as I can see that the filter code could possibly be overlooked by someone not familiar with anonymous methods, and think that we were acting on all the items in the list.

Yet again, I want to have my cake and eat it to.

However, as I said at the start this was an experiment to teach me a bit more about anonymous methods, so from that point of view it worked, and hopefully you got something out of it as well.

You can download the code from my delphi-experiments repository on github.

Nokia E71 – The good, the bad and the ugly

Photo by aresjoberg (used under Creative Commons) I’ve had a few weeks now of living with my new phone, a Nokia E71, including a couple of weeks of being on the road.
I haven’t owned a Symbian-based phone for many years, the last few phones I’ve had have been Windows-based, which despite what most people say, I’ve been pretty happy with. However, my last phone started to get flaky, so it was time to look for a new one.
I played with the 3G iPhone for quite awhile. While it is indeed as shmick as most people say, the biggest failing for me was the keyboard. I do almost as many emails on my phone as I do on my laptop, and I just couldn’t get used to the virtual keyboard.
In the end I was won over by the E71. It’s slim, has a great physical keyboard, and the battery life leaves my Windows phone for dead. Now that I’ve lived with it for a few weeks, I’m used to the Symbian OS and am actually kind of impressed with the thought that has gone into a lot of it. For example, just about everything I’ve wanted to find shortcut keys for I’ve been able to, which suggests this has had plenty of user input from previous versions put back into it.
However, there are a few big issues, but thankfully I’ve been able to find workarounds for each of them:

  • Astonishingly, there is no USB charger included in the box. Yes, there is a USB cable, but it is for communicating between your phone and your PC, not for charging. WTF? I travel a lot, and I try to travel light, so I try not to take any device with me that can’t be charged by hooking it up to my laptop. Otherwise I’d be lugging around (and no doubt losing) half a dozen power adapters. I went to the local phone accessories store and they wanted to charge me AU$80 for a genuine Nokia USB charger. Thankfully, I found someone selling third-party ones on EBay for AU$2.49 plus a couple of bucks postage. I bought two for under $10 total 🙂
  • The web browser is awful. Maybe that’s unfair, maybe they were going for "retro" as it feels like using Netscape back in the 90’s. Thankfully, Opera Mini rode to the rescue. This thing is free and full of awesome-ness. Why the hell would Nokia write their own, they should just cut a deal with Opera?
  • The included Nokia Maps app for the GPS looks like it was written by the same group who did the browser. Standing out in an open area it took 8 minutes to lock onto enough satellites to show me my location. Download Google Maps. I can start it, find my location, search for an address and start navigating in about a minute. In that time the Nokia app has just got past the splash screen.

With these additions, the E71 is possibly the best phone I’ve used. Without them, it’s got a lot of potential but doesn’t quite get there.

Beavis Board

Maybe you liked the idea of the Line6 Tonecore DSP thing that I posted about, but the coding part scared you off? Hmmmm, well, maybe the Beavis Board is for you.

It’s essentially the same thing, only with breadboards and a bagful of electronic components to do the audio hacking instead of code. Still tempting to be honest, but I need to finish some of the projects I’ve half done now before I add any more things to the list. Still, would be a cool way for someone to learn more about electronics.