Lazarus

Free Pascal => Beginners => Topic started by: beria on September 20, 2022, 07:08:54 am

Title: Is there an error in the description of the "record"?
Post by: beria on September 20, 2022, 07:08:54 am
Read the manual: https://www.freepascal.org/docs-html/ref/refse61.html#x120-1440009.1 (https://www.freepascal.org/docs-html/ref/refse61.html#x120-1440009.1) - " Constructors or destructors cannot be defined."

Code: Pascal  [Select][+][-]
  1. datao = record
  2.     Data: Pointer;
  3.     constructor Init(AData: longint);
  4.     procedure Done;
  5.   end;
  6.  
  7.  
  8. constructor datao.Init(AData: longint);
  9.   begin
  10.     GetMem(Data, AData);
  11.   end;
  12.  
  13.   procedure datao.Done;
  14.   begin
  15.     FreeMem(Data);
  16.   end;      
  17. -------
  18. var
  19. d: datao;
  20.  
  21. begin
  22. New(d);
  23.  d^ := datao.Init(250);
  24.  d.Done;
  25.  Dispose(d)
  26. end.
  27.  

The code compiles and works
Title: Re: Is there an error in the description of the "record"?
Post by: bytebites on September 20, 2022, 07:29:43 am
It doesn't compile
Quote
project1.lpr(1,6) Error: Syntax error, "BEGIN" expected but "identifier DATAO" found
Title: Re: Is there an error in the description of the "record"?
Post by: egsuh on September 20, 2022, 07:30:35 am
constructor is just for initialization purpose. Memory is automatically allocated (and freed) within stack by definition of a record variable, which is different from objects' construction which allocates memory within heap area.
Title: Re: Is there an error in the description of the "record"?
Post by: beria on September 20, 2022, 07:35:49 am
It doesn't compile
Quote
project1.lpr(1,6) Error: Syntax error, "BEGIN" expected but "identifier DATAO" found
you have advanced syntax turned off
Title: Re: Is there an error in the description of the "record"?
Post by: bytebites on September 20, 2022, 07:53:01 am
Code: Pascal  [Select][+][-]
  1. {$mode delphi}
  2. type
  3. datao = record
  4.     Data: Pointer;
  5.     constructor Init(AData: longint);
  6.     procedure Done;
  7.   end;
  8.  
  9.  
  10. constructor datao.Init(AData: longint);
  11.   begin
  12.     GetMem(Data, AData);
  13.   end;
  14.  
  15.   procedure datao.Done;
  16.   begin
  17.     FreeMem(Data);
  18.   end;
  19.  
  20. var
  21. d: datao;
  22.  
  23. begin
  24. New(d);
  25.  d^ := datao.Init(250);
  26.  d.Done;
  27.  Dispose(d)
  28. end.
  29.  

Quote
project1.lpr(24,6) Error: Pointer type expected, but got "datao"
project1.lpr(25,5) Error: Illegal qualifier
project1.lpr(27,11) Error: Pointer type expected, but got "datao"

Title: Re: Is there an error in the description of the "record"?
Post by: Thaddy on September 20, 2022, 08:09:02 am
constructor is just for initialization purpose. Memory is automatically allocated (and freed) within stack by definition of a record variable, which is different from objects' construction which allocates memory within heap area.
Initialization on advanced records can be done through the Initialize class operator too.
Example with both options:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$modeswitch advancedrecords}{$H+}
  2. type
  3.    TMyRec = record
  4.    test: integer;
  5.    constructor init(value:integer);
  6.    class operator initialize(var rec:TMyrec);
  7.    end;
  8.    // This applies to a single instance and is explicit
  9.    constructor TMyrec.init(value:integer);
  10.    begin
  11.      test := 1;
  12.    end;
  13.    // this applies to ALL instances and is implicit
  14.    class operator TMyRec.Initialize(var rec:TMyRec);
  15.    begin
  16.      rec.test:=100;
  17.    end;
  18. var
  19.   R:TMyrec;
  20. begin
  21.   writeln(R.test);    // initialized through class operator, value = 100, implicit.
  22.   R := TMyRec.Init(1);// initialized through constructor, value is 1, explicit.
  23.   writeln(R.test);
  24.   readln;
  25. end.

So the documentation seems indeed wrong/ not complete. IMHO the best option is the operator approach.
You can file a bug against documentation.

(Note, better use allocmem (clean memory) instead of getmem (dirty memory). )
Title: Re: Is there an error in the description of the "record"?
Post by: beria on September 20, 2022, 08:25:24 am

Quote
project1.lpr(24,6) Error: Pointer type expected, but got "datao"
project1.lpr(25,5) Error: Illegal qualifier
project1.lpr(27,11) Error: Pointer type expected, but got "datao"

you have advanced syntax turned off 
{$modeswitch advancedrecords}
{$pointermath on}
Title: Re: Is there an error in the description of the "record"?
Post by: Thaddy on September 20, 2022, 08:32:54 am
No, he uses {$mode delphi}, where advancedrecords is always on. Pointermath is in that example not necessary.
E.g. you can replace {$mode objfpc}{$modeswitch advancedrecords} in my example with simply {$mode delphi}

The only error is that your code is 32 bit centric because of longint. It should be ptrUint, so the code also works on 64 bit.
It should also be an unsigned type, since you can not allocate negative memory.
You should also use Allocmem instead of getmem. For heap allocated instances (globals) this does not matter too much since the heap is guaranteed to be initialized to all zero's, at least with the default memory manager, but for stack allocated instances this is important since stack allocated memory is dirty. See:
https://www.freepascal.org/docs-html/rtl/system/getmem.html where it is obvious it should be ptrUint.

I hope my example is clear enough.
Title: Re: Is there an error in the description of the "record"?
Post by: PascalDragon on September 20, 2022, 08:50:50 am
Read the manual: https://www.freepascal.org/docs-html/ref/refse61.html#x120-1440009.1 (https://www.freepascal.org/docs-html/ref/refse61.html#x120-1440009.1) - " Constructors or destructors cannot be defined."

That is a leftover from the time when the compiler indeed did not support constructors. Please report a bug against the documentation (https://gitlab.com/freepascal.org/fpc/documentation/-/issues).
Title: Re: Is there an error in the description of the "record"?
Post by: Thaddy on September 20, 2022, 08:53:52 am
Note that destructors are (maybe still) not supported, though.

And just in case:
Your original code should be corrected like so:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$modeswitch advancedrecords} // or simply only {$mode delphi}
  2. type
  3. pdatao= ^datao;
  4. datao = record
  5.     Data: Pointer;
  6.     constructor Init(AData: PtrUint);
  7.     procedure Done;
  8.   end;
  9.  
  10.  
  11. constructor datao.Init(AData: PtrUint);
  12.   begin
  13.     Data := AllocMem(AData);
  14.   end;
  15.  
  16.   procedure datao.Done;
  17.   begin
  18.     FreeMem(Data);
  19.   end;      
  20.  
  21. var
  22. d: pdatao;
  23.  
  24. begin
  25.  New(d);
  26.  d^ := datao.Init(250);
  27.  d^.Done;
  28.  Freemem(d);
  29. end.
Title: Re: Is there an error in the description of the "record"?
Post by: jcmontherock on September 20, 2022, 10:13:06 am
Why not using more simply a class ?
Title: Re: Is there an error in the description of the "record"?
Post by: KodeZwerg on September 20, 2022, 11:34:31 am
Initialization on advanced records can be done through the Initialize class operator too.
Thank you for that, i did not knew that too!
Why not using more simply a class ?
Because a record, as soon as out of scope, produce no memory leaks. At least thats how i learned it.
Title: Re: Is there an error in the description of the "record"?
Post by: Thaddy on September 20, 2022, 11:57:21 am
Note you can also have automatic finalize, see: https://wiki.freepascal.org/management_operators#Finalize
If you want I can rewrite your code with both class operators.
Title: Re: Is there an error in the description of the "record"?
Post by: KodeZwerg on September 20, 2022, 12:10:36 pm
Note you can also have automatic finalize, see: https://wiki.freepascal.org/management_operators#Finalize
Wow, that is crazy cool!!! Will use both in my next projects since i also do like records a lot (not like above code, more the simple ones where i need initialization, and now with finalization it really rocks to clean up my stuff, even if not needed)
Title: Re: Is there an error in the description of the "record"?
Post by: Thaddy on September 20, 2022, 12:22:54 pm
This is how to write it with the implicit initialize/finalize. You may still want to add the explicit constructor:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$modeswitch advancedrecords} // or simply only {$mode delphi}
  2. type
  3.   pdatao= ^datao;
  4.   datao = record
  5.     Data: Pointer;
  6.     constructor init(size:PtrUint);
  7.     Class operator initialize(var rec:datao);
  8.     Class operator finalize(var rec:datao);
  9.   end;
  10.  
  11.   constructor datao.init(size:ptrUint);
  12.   begin
  13.     Data := ReAllocMem(Data,size);  // realloc, because initialize has already allocated data.
  14.   end;
  15.  
  16.   class operator datao.initialize(var rec:datao);
  17.   begin
  18.     rec.Data := AllocMem(250);
  19.     writeln('At initialization 250 bytes allocated for data');
  20.   end;
  21.  
  22.   class operator datao.finalize(var rec:datao);
  23.   begin
  24.     FreeMem(Rec.Data);
  25.     writeln('At finalization, data is free''d');    
  26.   end;      
  27.  
  28. var
  29.   d: pdatao;
  30. begin
  31.   New(d);
  32.   Dispose(d);
  33. end.
Title: Re: Is there an error in the description of the "record"?
Post by: beria on September 20, 2022, 04:56:18 pm
This is how to write it with the implicit initialize/finalize. You may still want to add the explicit constructor:
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}{$modeswitch advancedrecords} // or simply only {$mode delphi}
  2. type
  3.   pdatao= ^datao;
  4.   datao = record
  5.     Data: Pointer;
  6.     constructor init(size:PtrUint);
  7.     Class operator initialize(var rec:datao);
  8.     Class operator finalize(var rec:datao);
  9.   end;
  10.  
  11.   constructor datao.init(size:ptrUint);
  12.   begin
  13.     Data := ReAllocMem(Data,size);  // realloc, because initialize has already allocated data.
  14.   end;
  15.  
  16.   class operator datao.initialize(var rec:datao);
  17.   begin
  18.     rec.Data := AllocMem(250);
  19.     writeln('At initialization 250 bytes allocated for data');
  20.   end;
  21.  
  22.   class operator datao.finalize(var rec:datao);
  23.   begin
  24.     FreeMem(Rec.Data);
  25.     writeln('At finalization, data is free''d');    
  26.   end;      
  27.  
  28. var
  29.   d: pdatao;
  30. begin
  31.   New(d);
  32.   Dispose(d);
  33. end.

I didn't know you could do that. Thank you. It's the perfect way.

Title: Re: Is there an error in the description of the "record"?
Post by: KodeZwerg on September 20, 2022, 05:12:01 pm
This is how to write it with the implicit initialize/finalize. You may still want to add the explicit constructor
Thanks a lot for your snippet!
Title: Re: Is there an error in the description of the "record"?
Post by: Thaddy on September 21, 2022, 08:46:54 am
As it is, I discovered a bug that caused a double free if you call dispose instead of freemem because data should be set to nil after the call to freemem.
I corrected that code, with an example call to the explicit constructor.
Code: Pascal  [Select][+][-]
  1. {$mode delphi} // or {$mode objfpc}{$modeswitch advancedrecords}
  2. type
  3.   pdatao= ^datao;
  4.   datao = record
  5.     Data: Pointer;
  6.     constructor init(size:PtrUint);
  7.     Class operator initialize(var rec:datao);
  8.     Class operator finalize(var rec:datao);
  9.   end;
  10.  
  11.   constructor datao.init(size:ptrUint);
  12.   begin
  13.      ReAllocMem(Data,size);  // realloc, because initialize has already allocated data.
  14.   end;
  15.  
  16.   class operator datao.initialize(var rec:datao);
  17.   begin
  18.     rec.Data := AllocMem(250);
  19.     writeln('At initialization 250 bytes allocated for data');
  20.   end;
  21.  
  22.   class operator datao.finalize(var rec:datao);
  23.   begin
  24.     FreeMem(Rec.Data);
  25.     Rec.Data := nil;  // otherwise double free.
  26.     writeln('At finalization data is free''d');
  27.   end;      
  28.  
  29. var
  30.   d: pdatao;
  31. begin
  32.   New(d);
  33.   d^.init(400);
  34.   dispose(d);
  35. end.
The bug was only using the explicit constructor.
There is still a peculiarity, though:
If you define an explicit constructor, finalize is called twice, harmless but superfluous.
If you change the explicit constructor to a procedure this does not happen.

Maybe PascalDragon can shed some light on it.
Title: Re: Is there an error in the description of the "record"?
Post by: ASerge on September 21, 2022, 08:29:21 pm
As it is, I discovered a bug that caused a double free if you call dispose instead of freemem because data should be set to nil after the call to freemem.
Dispose call Finalize, FreeMem not.
Title: Re: Is there an error in the description of the "record"?
Post by: Thaddy on September 21, 2022, 08:47:25 pm
Indeed, so I fixed it by procedure instead of constructor. Actually it is a work-around, because calling the finalize twice - in the context of management operators - is a known issue and there is an acknowleged and still outstanding bug report for it. It is harmless except for speed. You can check the behavior with heaptrc, btw.
TinyPortal © 2005-2018