Wed, 19 Nov 2008

More Delphi Type Hate

I have simple needs. I have a base class with some generic behavior and subclasses with specific information for that generic behavior. More concretely, the subclasses need to provide the generic behavior with an ordered list of things that designate key fields on database tables. The best representation of those "things" in Delphi seems to be members of an enumeration:

type
  TKeyField = (kfFoo, kfBar, kfBaz, kfQuux);

Since I need the list of fields to be ordered, I need them in an array:

type
  TKeyFieldArray = array of TKeyField;

The declaration of the base class is pretty simple:

type
  TBaseClass = class
   protected
    function GetKeyFieldList : TKeyFieldArray; virtual; abstract;
   public
    procedure DoSomethingWithKeyFields;
  end;

As is the declaration of the subclass:

type
  TSubClass = class(TBaseClass)
   protected
    function GetKeyFieldList : TKeyFieldArray; override;
  end;

So where's the problem? Where's the hate? The hate is in the implementation. If Delphi had array literals, this would be easy. Something like:

function TSubClass.GetKeyFieldList : TKeyFieldArray;
begin
  Result := [kfBar, kfFoo, kfQuux];
end;

But it doesn't. It has some special magic for array literals if they're the parameter to a function, but not anywhere else. It does, however, have a syntax for array constants. Perhaps this will work:

function TSubClass.GetKeyFieldList : TKeyFieldArray;
  const
    keyFieldList : TKeyFieldArray = (kfBar, kfFoo, kfQuux);
begin
  Result := keyFieldList;
end;

But no. That TKeyFieldArray is a dynamic array; Delphi doesn't allocate any space for it, so it can't be a constant value. You have to tell Delphi how big each constant array is, even though you're already telling it how many elements are in the array. So perhaps this is the solution:

function TSubClass.GetKeyFieldList : TKeyFieldArray;
  const
    keyFieldList : array[0..2] of TKeyField = (kfBar, kfFoo, kfQuux);
begin
  Result := keyFieldList;
end;

But no. Because of Delphi's approach to static typing, those are actually different types, and are therefore not assignment-compatible. (See previous hates on this subject.) No, here is the code that Delphi makes me type for what should be a one-line function implementation:

function TSubClass.GetKeyFieldList : TKeyFieldArray;
begin
  SetLength(Result, 3);
  Result[0] := kfBar;
  Result[1] := kfFoo;
  Result[2] := kfQuux;
end;

And just earlier this morning I was pleased because I read that Delphi 2007 (to which I'll soon be upgrading from Delphi 5) has for...in loops, so I can finally have foreach. (Can't get the generics and anonymous functions in Delphi 2009, because we need .NET and that's not yet available for Delphi 2009.) Oh, Delphi. The one hand giveth, and the entire rest of the stupid, anemic, pox-ridden language taketh away.


Phil! Gold