DeHL 0.8.3

DeHL

I will be brief as usual — version 0.8.3 of DeHL is out. The downloads can be found on this page and changelog on this page. This release “fixes” some of the things I wanted fixed for a long time, so it seemed this is the perfect moment for this to happen. A new unit is introduced — DeHL.Tuples — which brings seven generic Tuple<…> types. I have also finished moving away from Integer and Cardinal to NativeInt and NativeUInt through all DeHL.

Breaking changes are:

  1. DeHL.Converter was renamed to DeHL.Conversion. This name change was done mostly to reflect the nature of the unit. It does not contain one simple class anymore. Now there is a fully featured conversion system.
  2. TBinaryTree<T> (in DeHL.Collections.BinaryTree) is no more. It was written way in the beginnings of the project and was buggy, incomplete and utterly useless.
  3. And the most visible and breaking of all changes is the removal of TKeyValuePair<TKey, TValue> (in DeHL.KeyValuePair). It was replaced with KVPair<TKey, TValue> (in DeHL.Tuples). The easiet way to get over this change is to find and replace all TKeyValuePair instances with KVPair and all DeHL.KeyValuePair uses with DeHL.Tuples.

Now, obviously an example using Tuples:

uses
  SysUtils, DeHL.Tuples;

{ If you are lazy and do not wish to declare a new record type
  to be used as result. Use Tuple<..> do to that. }
function GiveMeSomeData(): Tuple<String, Integer, Integer>;
begin
  { ... Do some processing ... }
  Result := Tuple.Create('Some data', 100, -99);
end;

var
  LResult: Tuple<String, Integer, Integer>;
begin
  { Obtain the result }
  LResult := GiveMeSomeData();

  { And write the results to the console }
  with LResult do
    WriteLn('Something was done with result: ', Value1, ', ', 
      Value2, ', ', Value3, '!');
end.

The new conversion engine handles most of the possible conversions and also allows registering custom ones:

uses
  SysUtils,
  DeHL.Conversion,
  DeHL.Collections.List;

type
  { Declare some type that cannot be converted into integer directly.
    We're making up an "int". }
  TRecordInt = packed record
    FValueAsString: string;
    FSign: Boolean;

    constructor Create(const AInt: Integer);
  end;

{ TRecordInt }

constructor TRecordInt.Create(const AInt: Integer);
begin
  { Decompose an int }
  FValueAsString := IntToStr(Abs(AInt));
  FSign := (AInt < 0);
end;

var
  LInputList: TList<TRecordInt>;
  S: String;
  I: Integer;
begin
  { Create a list of TRecordInt }
  LInputList := TList<TRecordInt>.Create();

  { Fill it with some random values (positive and negative) }
  for I := 0 to 10 do
    LInputList.Add(TRecordInt.Create(Random(MaxInt) - (MaxInt div 2)));

  { Now comes the interesting part ... register a custom converter
    from TRecordInt to Integer }
  TConverter<TRecordInt, Integer>.Method :=
    function(const AIn: TRecordInt; out AOut: Integer): Boolean
    begin
      { Convert the TRecordInt back to an integer }
      Result := TryStrToInt(AIn.FValueAsString, AOut);
      if Result and AIn.FSign then
        AOut := -AOut;
    end;

  { Now print the values to the console. Convert them from TRecordInt to
    Integer then to String }
  for S in LInputList.Op.Cast<Integer>.Op.Cast<String> do
    WriteLn(S);
end.

TConverter is also smart enough to figure out that “type MyInt = type Integer” is actually equivalent to Integer. If there is no explicit custom conversion method registered for it the converter for the standard type will be selected is possible. In the worst case, when TConverter cannot convert directly between the given types, it falls back to Variant conversion (using TType.TryConvertToVariant and TType.TryConvertFromVariant) which all types registered with DeHL’s type system, if possible, should implement.

Well, that’s all for today,
Have Fun!