Question

There appears to be a difference between the default sorting of a TPair when compiling under 32 bit vs 64 bit. Under 32 bit, the default sort behaves like it is sorting on the Key of the pair, under 64 bit, it looks to sort by the Value. Is this to be expected, or am I missing something ?

Tested using Delphi XE2, update 4. In a VCL application, drop a button, checkbox and listbox onto the screen, declare the following type

type TPairComparer = TComparer<TPair<integer,integer>>;

then place the code below in the button OnClick and run. Under 32 bit, using the default sort, the pairs are listed by key i.e. 1,2,3,4,5,6. Under 64 bit, the pairs are listed by value i.e. 2,5,6,7,8,9 and NOT by key.

To make the code work consistently across both platforms I have needed to specify my own comparer to force the sort by key on a 64 bit executable.

 procedure TForm1.Button1Click(Sender: TObject);
 var PriorityList           : TList<TPair<Integer,integer>>;
     Pair                   : TPair<integer,integer>;
     PairComparer           : IComparer<TPair<integer,integer>>;
     iLoop                  : integer;
 begin

 PriorityList := TList<TPair<Integer,integer>>.Create;

 PairComparer := TPairComparer.Construct(
 function(const Left, Right: TPair<integer,integer>): Integer
 begin
      case Left.Key = Right.Key of
           true : Result := 0;
      else        case Left.Key < Right.Key of
                       true : Result := -1;
                  else        Result := +1;
                  end;
      end;
 end);

 Pair.Key   := 6;
 Pair.Value := 6;
 PriorityList.Add(Pair);

 Pair.Key   := 5;
 Pair.Value := 5;
 PriorityList.Add(Pair);

 Pair.Key   := 4;
 Pair.Value := 8;
 PriorityList.Add(Pair);

 Pair.Key   := 3;
 Pair.Value := 9;
 PriorityList.Add(Pair);

 Pair.Key   := 2;
 Pair.Value := 7;
 PriorityList.Add(Pair);

 Pair.Key   := 1;
 Pair.Value := 2;
 PriorityList.Add(Pair);

 case Checkbox1.Checked of
      true  : PriorityList.Sort;
      false : PriorityList.Sort(PairComparer);
 end;

 ListBox1.Clear;
 for iLoop :=  0 to PriorityList.Count-1 do
     ListBox1.Items.Add(Format('Key : %d , Value : %d',[PriorityList[iLoop].Key,PriorityList[iLoop].Value]));

end;

Was it helpful?

Solution

The default comparer for such a type is pretty arbitrary. The compiler doesn't use any knowledge of the make up of the record. You've certainly not told it which member you want to be the primary sort key. So it is free to do what it wants.

In 32 bit the default comparer for an 8 byte record is implemented with a call to a CompareMem-like function. So bytes are compared in increasing order of address. Thus the key is more significant.

Under 64 bit the default comparer treats the type as an unsigned 64 bit integer and so bytes are compared in decreasing address order, this being little endian. And so the value is more significant.

The pertinent code is in the implementation section of Generics.Defaults:

function Comparer_Selector_Binary(info: PTypeInfo; size: Integer): Pointer;
begin
  case size of
    // NOTE: Little-endianness may cause counterintuitive results,
    // but the results will at least be consistent.
    1: Result := @Comparer_Instance_U1;
    2: Result := @Comparer_Instance_U2;
    4: Result := @Comparer_Instance_U4;
    {$IFDEF CPUX64}
    // 64-bit will pass const args in registers
    8: Result := @Comparer_Instance_U8;
    {$ENDIF}
  else
    Result := MakeInstance(@Comparer_Vtable_Binary, size);
  end;
end;

The bottom line is that you need to supply a real comparer whenever you wish to sort records. Only built in numeric and string types have well defined ordered comparers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top