#1 2024-09-30 12:01:52

WladiD
Member
From: Germany
Registered: 2010-10-27
Posts: 39

Exception with generic list and custom compare function

Hi ab!

Recently we had upgraded from mORMot 2.0.5027 to the latest stable 2.2.6584. Since then, we've encountered an exception in some parts of our code "x.AddSorted() is not allowed with loCreateUniqueIndex: use Add()". This occurs when using generic lists with AddSorted and a custom compare function.

Here is an example, how to reproduce the exception:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Math,
  Mormot.core.collections;

type
  TTestRecord = record
    Test: Integer;
  end;

function SomeComparer(const Item1, Item2): Integer;
begin
  Result:=CompareValue(TTestRecord(Item1).Test,TTestRecord(Item2).Test);
end;

var
  ListUnderTest: IList<TTestRecord>;
begin
  try
    ListUnderTest:=Collections.NewPlainList<TTestRecord>([]);
    ListUnderTest.Comparer:=SomeComparer;

    var RecA:=Default(TTestRecord);
    RecA.Test:=321;
    var RecB:=Default(TTestRecord);
    RecB.Test:=123;

    ListUnderTest.AddSorted(RecA);
    ListUnderTest.AddSorted(RecB);

    for var LoopRec in ListUnderTest do
    begin
      Writeln(LoopRec.Test);
    end;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  readln;
end.

The output of this console example is:

EIList: TIList<Project1.TTestRecord>.AddSorted() is not allowed  with loCreateUniqueIndex: use Add()

We analyzed the cause and believe the check in mormot.core.collections (specifically in TIListParent.DoAddSorted) is incomplete:

function TIListParent.DoAddSorted(const value; wasadded: PBoolean): integer;
begin
  if fHasher = nil then // << this check don't consider the compare function
    raise EIList.CreateUtf8('%.AddSorted() is not allowed  with ' +
      'loCreateUniqueIndex: use Add()', [self]);
  result := fDynArray.FastLocateOrAddSorted(value, wasadded);
end;

Instead, the check should be:

function TIListParent.DoAddSorted(const value; wasadded: PBoolean): integer;
begin
  if (fHasher = nil) and (@fDynArray.compare = nil) then // << consider also the compare function
    raise EIList.CreateUtf8('%.AddSorted() is not allowed  with ' +
      'loCreateUniqueIndex: use Add()', [self]);
  result := fDynArray.FastLocateOrAddSorted(value, wasadded);
end;

Many thanks for your time and consideration!

Best regards from Germany

Offline

#2 2024-09-30 12:39:49

WladiD
Member
From: Germany
Registered: 2010-10-27
Posts: 39

Re: Exception with generic list and custom compare function

Notice:
Previously the check was if fHasher<>nil and changed to if fHasher = nil in
commit 51627ba2a2d2b70c9604115cf36cd367c59e1b29.
Maybe it was correct previously?

Last edited by WladiD (2024-09-30 12:49:56)

Offline

#3 2024-09-30 13:23:51

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,552
Website

Re: Exception with generic list and custom compare function

You are right: this was a regression.

Please try with
https://github.com/synopse/mORMot2/commit/03c3434d

Thanks a lot Wladi for the detailed report and investigation!
smile

Offline

#4 2024-09-30 14:32:30

WladiD
Member
From: Germany
Registered: 2010-10-27
Posts: 39

Re: Exception with generic list and custom compare function

This works for us.

Thank you so much for your consistently fast and reliable support!

Offline

Board footer

Powered by FluxBB