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!

You May Also Like

About the Author: Alexandru Ciobanu

20 Comments

  1. I’ve decided to try out your library. In the past I’ve made use of Delphi’s built-in TObjectDictionary, like this: TMyObjectList = class(TObjectDictionary)…etc.

    When I try to compile using your implementation of TObjectDictionary, I get “[DCC Error] myunit.pas(##): E2511 Type parameter ‘TKey’ must be a class type”

    I see in your source that you’ve declared TObjectDictionary like this: TObjectDictionary… (with the class specifier).

    How can I continue to use strings as Keys in my object lists?

    Thanks, Vic.

  2. Sorry, it looks like WordPress is eating my angle brackets. Anyway, what I’m trying to do is use a string for the TKey parameter. Hope that helps.

  3. Yes, indeed. TObjectDictionary in DeHL is limited to two classes only. You can make your own with only values being objects. See how TObjectDictionary is implemented. It’s really simple and in fact it just wraps some basic functionality.

  4. Feature request…

    DeHL.Math.Money

    Class would be nice, with proper handing of $1.00 / 3 = 0.33 + 0.33 + 0.34 or rounding errors. Similarly.. In countries which don’t have 1c or 2c and minimum coin is 5c.

    Anyway.. Just a thought

  5. @Andrew Tierney
    I’m basing it’s interface on Java’s BigDecimal — it looks much better than the others I’ve seen.
    Hope to make it more “Delphi-sh”.

    Also note that it is BigDecimal and not Decimal.

  6. Hello,

    I’m having an issue with your library (0.8.3). The Delphi IDE stops responding at times when Code Completion is invoked. At first I thought it was something else, like an addin or installed package or bad library path. But it seems to be something in your library. The following code same reproduces the problem on my machine (Win 7 Ult x64, D2010 Ent):

    { CODE START }
    unit Unit2;

    interface

    uses
    //Use one or the other, not both. See note towards the end of code.
    DeHL.Collections.List; // When this line is uncommented, code completion fails
    //Generics.Collections; // When this line is uncommented, code completion works

    type
    TMyEnum = (meAAA, meBBB, meCCC);

    TMyObject = class(TObject)
    strict private
    FName: string;
    FMyEnum: TMyEnum;
    public
    property Firstname: string read FName write FName;
    property MyEnum: TMyEnum read FMyEnum write FMyEnum;
    constructor Create;
    end;

    TMyContainer = class(TList)
    end;

    implementation

    { TMyObject }

    constructor TMyObject.Create;
    begin
    FName := ”;
    FMyEnum := m // <– invoking code completion (Ctrl+Space) here hangs the IDE
    end;

    end.
    { CODE END }

    Invoking code completion in the constructor, on the enumeration causes the IDE to stop responding. In process explorer, the bds.exe process runs at 50%CPU, while the Private bytes/Working Set continues to climb and climb. After about 8-9 minutes, it stops and the IDE becomes responsive again, but the process repeats itself if code completion is invoked again.

    Can you or anyone else verify this problem, and perhaps a solution?? I would like to use your library, but this issue prevents that.

    Vic

  7. I see in Quality Central report #85788 is very similar. I added a comment showing this way of reproducing the issue. Hopefully a workaround or fix will be available soon.

  8. @alex
    Already found something about the hanging Code Completion? Because it is not usable for me!
    (and I hate that because I am really enthusiast about DeHL!)

  9. I am sorry but I cannot recommend anything for D2009 or D2010. You can try to include DeHL not as a package but as source files directly. That might help.

  10. Are you still updating DeHl ? or do you recommend people move to the Collections 1.1 library ?

    Are the collectiosn in DeHl and Collections 1.1 similar with DeHL having String Extensions etc ?

    Thanks

  11. I’m trying to get Collections 1.2 out which will be the first truly “stable” release. Then I will consider going back to DeHL and update it.

    Being one man doesn’t really help …

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.