Jump to content
Alexander Halser

Custom sort of a TList with object references (works in 32 bit, does not in 64 bit)

Recommended Posts

I am currently working on some legacy code that uses simple TLists with object references and custom sort. 

This has worked and still works perfectly in 32 bit. But in 64 bit output, the list is not sorted (correctly), although it compiles without any errors.

 

The form contains a couple of TShapes with different widths. The code below is supposed to sort and rearrange them by width.

In 32 bit, the sort is ok. In 64 bit it's random.

In fact, the function SortByWidth is called with item1 and item2 being the same (here: pointing to the same TShape).

 

I am concerned and want to understand what's going on and why this does not work in 64 bit.

There are more modern ways to sort a TList or use dictionaries. But this is legacy code that contains sorting functions of this kind in several places. 

 

procedure TForm2.BtnSortClick(Sender: TObject);

  function SortByWidth(const item1, item2: TShape): Integer;
  begin
    result := item1.width - item2.width;
  end;

var
  AList: TList;
  i: Integer;
begin
  AList := TList.Create;
  try
    for i := 0 to ComponentCount-1 do
      if (Components[i] is TShape) then
        AList.Add(Components[i]);

    AList.Sort(@SortByWidth);   //what is wrong about this in 64 bit?

    for i := 0 to AList.Count-1 do
      TShape(AList[i]).Top := 30 + (40 * i);
  finally
    AList.Free;
  end;
end;


 

compare32.png

compare64.png

Share this post


Link to post

@Alexander Halser

 

try this..

uses
  Generics.Defaults;

var
  intList: TList<Integer>;
begin
  intList := TList<Integer>.Create;
  try
    // Add some integer values to the list
    intList.Add(3);
    intList.Add(1);
    intList.Add(7);
    intList.Add(4);

    // Sort the list in ascending order
    intList.Sort(TComparer<Integer>.Construct(
      function(const Left, Right: Integer): Integer
      begin
        if Left < Right then
          Result := -1
        else if Left > Right then
          Result := 1
        else
          Result := 0;
      end));

    // Do something with the sorted list
    // ...
  finally
    intList.Free;
  end;
end.

 

 

Share this post


Link to post

It's not TList<T> from System.Generics.Collections.

It's the age-old TList from System.Classes.

 

Straight from the documentation:

System.Classes.TList.Sort - RAD Studio API Documentation (embarcadero.com)

 

Quote

Note: Local functions on Win64 involve an extra hidden parameter. Therefore, local functions and procedures are not suitable for use with TList.Sort and similar methods requiring parameters representing procedure references.

 

Edited by Der schöne Günther
  • Like 2

Share this post


Link to post

The hack with the @ operator works only in Win32, but Win64 has different calling conventions. Use a global function for the Sort method (matching  the signature) instead of a local one (which does not).

  • Like 3

Share this post


Link to post

@Uwe Raabe & Günter: Thank you, that does the trick! This saves me a lot of code-rewriting 🙂

 

 

function SortShapesByWidth(const item1, item2: TShape): Integer;
begin
  result := item1.width - item2.width;
end;

procedure TForm2.BtnSortClick(Sender: TObject);
var
  AList: TList;
  i: Integer;
begin
  AList := TList.Create;
  try
    for i := 0 to ComponentCount-1 do
      if (Components[i] is TShape) then
        AList.Add(Components[i]);

    AList.Sort(@SortShapesByWidth);

    for i := 0 to AList.Count-1 do
      TShape(AList[i]).Top := 30 + (40 * i);
  finally
    AList.Free;
  end;
end;


 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×