See, the developers of the Delphi compiler, nothing is impossible.

See, the developers of the Delphi compiler, nothing is impossible.

Originally shared by Maciej Izak

Hi,

I'm pleased to finally announce the additional record operators: Initialize, Finalize.

Download (r33200): http://svn.freepascal.org/svn/fpc/branches/maciej/smart_pointers

"Record management operators aka record constructor/destructor"

They working like low level auto-executed constructor/destructor for records.

===== motivation =====

It can be used for:

-nullable types
-very fast RTTI.TValue implementation
-as replacement for manually called Init/Finit record methods for example like in mORMot for many types.
-auto init/finit for fields of pointer/object type

Additionally, it is base for my further work:

-smart pointers
-ARC objects
-Oxygene mode
-Delphi NextGen mode

===== example begin =====
{$MODE DELPHI}
uses SysUtils;

type
  TFoo = record
  private
    class operator Initialize(var aFoo: TFoo);
    class operator Finalize(var aFoo: TFoo);
  public
    F: Integer;
    S: string;
    P: Pointer;
    C: TObject
  end;

class operator TFoo.Initialize(var aFoo: TFoo);
begin
  aFoo.F := 10;
  aFoo.S := 'FPC rulezzz';
  aFoo.P := nil;
  aFoo.C := nil;
end;

class operator TFoo.Finalize(var aFoo: TFoo);
begin
  if aFoo.S = 'Delphi rulezzz' then
    raise Exception.Create('Trololo');
  FreeMem(aFoo.P);
  aFoo.C.Free;
end;

procedure Foo;
var
  f: TFoo;
begin
  WriteLn(f.F); // output: 10
  WriteLn(f.S); // output: FPC rulezzz
  WriteLn(f.P = nil); // output: true
  WriteLn(f.C = nil); // output: true
end;

begin
  Foo;
end.
===== example end=====

===== more info =====

When Initialize of Finalize is used, record is treated as managed record (similar to record containg string, interface or dynamic array).

It works correctly in all possible ways, works for: 

* New
* Dispose
* Initialize
* Finalize
* InitializeArray
* FinalizeArray
* global variables
* local variables
* for fields inside: records, objects, classes

===== additional gain =====

Speed of dynamically created instances for records increased. For now is used always init table instead of rtti table:

type
  TBar = record
    f1,f2,f3,f4,f5,f6,f7,f8,f9: byte;
    s: string;
  end;

previously:

  GetMem(PB, SizeOf(TBar));
  InitializeArray(PB, TypeInfo(TBar), 1); // FPC_INITIALIZE was executed 10 times

now:

  GetMem(PB, SizeOf(TBar));
  InitializeArray(PB, TypeInfo(TBar), 1); // FPC_INITIALIZE is executed just once
 

===== more examples =====

http://svn.freepascal.org/cgi-bin/viewvc.cgi/branches/maciej/smart_pointers/tests/test/toperator90.pp?view=co&revision=33200&content-type=text%2Fplain&pathrev=33200

http://svn.freepascal.org/cgi-bin/viewvc.cgi/branches/maciej/smart_pointers/tests/test/toperator91.pp?view=co&revision=33200&content-type=text%2Fplain&pathrev=33200

http://svn.freepascal.org/cgi-bin/viewvc.cgi/branches/maciej/smart_pointers/tests/test/toperator92.pp?view=co&content-type=text%2Fplain&pathrev=33200

http://svn.freepascal.org/cgi-bin/viewvc.cgi/branches/maciej/smart_pointers/tests/test/toperator93.pp?view=co&content-type=text%2Fplain&pathrev=33200

===== more details from commit message =====

http://svn.freepascal.org/cgi-bin/viewvc.cgi/branches/maciej/smart_pointers/?view=log&sortby=date&pathrev=33200
http://svn.freepascal.org/svn/fpc/branches/maciej/smart_pointers

Comments

  1. Smart pointers have been possible in Delphi for a long time

    ReplyDelete
  2. David Heffernan By anonymous methods. That is workaround - not really smart ;). Approach by using records is more lightweight. Anyway at the moment we don't have smart pointers in FPC. Initialize/Finalize is just small step in this direction.

    ReplyDelete
  3. Maciej Izak Not smart? Why not? C++ doesn't bake these into the language, they are implemented on top of the language.

    Of course, smart pointers are much more important in C++ because of the RAII idiom. In C++ there is not try/finally so a different idiom is used to manage lifetime. Which means that smart pointers aren't as imperative for Delphi code.

    ReplyDelete
  4. David Heffernan There is no smart pointers in Delphi - in Delphi exist fat and slow smart pointer simulator. :) It is not smart because it means many useless additional calls, and memory consumption in comparison to C++ implementation (or FPC in near future). It will not be part of language (not at all) called smart pointers, just more proper approach for things called "smart pointers" (maybe with oxygene additional syntax sugar: unretained, weak, strong).

    ReplyDelete
  5. David Heffernan In addition it will be used for Delphi NEXTGEN mode (ARC) without breaking any compatibility for existing FPC code base and modes, unlike in Delphi NEXTGEN.

    ReplyDelete
  6. I can't quite bring myself to read all that source code. Perhaps what you say is true. Perhaps there's a real need for smart pointers that I am not aware of. Perhaps Delphi needs to adopt RAII.

    ReplyDelete
  7. The topic was not about smart pointers actually. It's about the records custom initializators/finalizators, which allow all kinds of useful things, not only smart pointers.

    ReplyDelete
  8. One of most voted feature request in QC is request of THIS feature (http://qc.embarcadero.com/wc/qcmain.aspx?d=21729). 10 years. Your opinion is important for us (C) Bor/CG/Emb.

    ReplyDelete
  9. Javier Hernández Actually, Delphi has record constructors.

    ReplyDelete
  10. Javier Hernández Not that I am aware of.

    ReplyDelete
  11. Uwe Raabe Javier Hernández the topic is not about record constructors/destructors but about management operators Initialize/Finalize which Delphi hasn't.

    ReplyDelete
  12. Uwe Raabe You can't have a default constructor, though, nor a destructor. Initialise/Finalize as class operators here seem (to me) to be names that apply to what happens, but don't fit the Delphi way of things. In Delphi, I'd like to see record constructors and destructors - or removal of the constructor keyword from records completely and have something like 'initializer' 'finalizer' instead, so the way you'd have a class's 'constructor TFoo.Create' you might have 'initializer TFoo.Init'. But IMO it's simpler to keep the existing keywords, and have them function rather than introducing new operators or new keywords.

    ReplyDelete
  13. David Millington​ I find them highly descriptive and proper. After all thats what the compiler calls for you or that you call yourself if you have a dynamically allocated array of records. And they fit with the Inc, Round etc operators that can also be overloaded.

    ReplyDelete
  14. David Millington removing constructors from records is bad idea. "Constructor" is part of using "New" intrinsic. For example:

    type
    PR = ^TR;
    TR = record
    constructor Create(A: Integer);
    end;
    ...
    var
    R: PR;
    begin
    R := New(PR, Create(10));

    ReplyDelete

Post a Comment