avk959 / LGenerics

Generic algorithms and data structures for Lazarus/Free Pascal
Apache License 2.0
113 stars 16 forks source link

LgHashMap Initialization issue: cannot be determined whether it is due to the compiler or LGenrics. #15

Closed uvwx413 closed 6 months ago

uvwx413 commented 6 months ago

Hello avk959! Thank you very much for excellent LGenerics library!

I encountered an issue when applying HashMap. but I cannot determine whether it is due to the compiler or LGenrics.

type
  TTestMapSpec=specialize TGLiteHashMapLP<string, string, string>;
  TTestMap=TTestMapSpec TMap;

  TFoo=class
  private
    FMap: TTestMap;
    procedure DoInitialize; // Here, some Key-Val will be inserted into FMap
    procedure DoFinalize;
  public
    constructor Create;  // Calling DoInitialize
    destroyer Destroy; override;
   end;

----TFoo is working properly. But if a TBar is derived from TFoo, such as:

  TBar=class (TFoo)
  public
    constructor Create; // Call TFoo.Create
    destroyer Destroy; override;
  end;

----Execute TBar.Create will result in a memory overflow. Tracking the code reveals that it is constantly calling TGLiteHashTableLP.Resize(aNewCapacity: SizeInt); The difference is: The "class operator TGLiteHashTableLP.Initialize(var ht: TGLiteHashTableLP)" is called before the body of the TFoo.Create while call TFoo.Create DIRECTLY, and it will be NOT while “TBar.Create ->TFoo.Create".

****so, it seems to be related to initialization, so if defined TTestMap=specialize TGHashMapLP<string, string>; Modify

   TFoo.DoInitialize;
    begin
      FMap := TTestMap.Create;  // add this line, do Initialization
      ...
    end;

--- both TFoo.Create and TBar.Create are working properly.

Test Enviroment: Win7-x64, Lazarus 3.2/FPC 3.2.2; LGenerics 0.56 or master version

the test code attached here, note that the conditional compilation fb.zip

 {$DEFINE LITE_VERSION}      // undefine this, both TFoo and TBar are working properly.
                                              // define LITE_VERSION, TFoo working properly, TBar go wrong.  

2024-04-25 22 13 27 2024-04-25 22 12 31 2024-04-25 22 13 07

avk959 commented 6 months ago

In this form, everything seems to work as expected.

foo_bar.zip

but I cannot determine whether it is due to the compiler or LGenrics.

So what will be the final verdict?

uvwx413 commented 6 months ago

Yes, LGenrics works well! and your "print()" is simple and elgent. Thanks for your help!

so, Explicitly initialize with “Initialize(FMap)” at base class constructor is nesscessary.

The Wiki page about management operators say:

The initialize operator is called directly after stack (or heap) memory allocation for a record happens.

it seems to be:
FMap's operator Initialize method is called implicitly while using ancestor TFoo object and NOT called while using derived TBar object

avk959 commented 6 months ago

FMap's operator Initialize method is called implicitly while using ancestor TFoo object and NOT called while using derived TBar object

I believe this is a compiler bug.