Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/Compiler/Delphi/Language    [ Add a report in this area ]  
Report #:  21729   Status: Open
Record Operator Overloading: Please implement "Initialize" and "Finalize" operators
Project:  Delphi Build #:  2144
Version:    10.0 Submitted By:   Pierre le Riche
Report Type:  Suggestion / Enhancement Request Date Reported:  11/25/2005 4:41:58 AM
Severity:    Infrequently encountered problem Last Updated: 3/20/2012 2:24:39 AM
Platform:    All platforms Internal Tracking #:   238125
Resolution: None (Resolution Comments) Resolved in Build: : None
Duplicate of:  None
Voting and Rating
Overall Rating: (14 Total Ratings)
4.93 out of 5
Total Votes: 277
At the moment, if a local variable record contains a reference counted field (long string, dynamic array, etc.) then the compiler inserts a call to InitializeRecord at the start of the procedure to zero-initialize the reference counted fields and a FinalizeRecord at the end of the procedure to free any memory allocated by them. Both these procedures make use of the RTTI to determine what exactly to do, and it results in slow code.

Since Delphi already has a mechanism for initializing and finalizing records, why not allow the programmer to specify these initializers and finalizers a la operator overloading?

Consider the following example:

  TMyString = record
    Data: AnsiString;
    class operator Add(const a, b: TMyString): TMyString;

procedure Test;
  x, y, z: TMyString;
  x := y + z;

The sequence of calls inside Test are:
InitializeRecord (3 times)
FinalizeArray (which calls FinalizeRecord 3 times)

Most of these calls are due to the fact that the TMyString.Data field is reference counted. Compare this to the following declaration:

  TMyString = record
    Data: Pointer;
    class operator Add(const a, b: TMyString): TMyString;
    class operator Initialize(const a: TMyString);
    class operator Finalize(const a: TMyString);

Data can now be a block of memory allocated through GetMem in "Initialize" and freed inside "Finalize".

The sequence of calls will now become:
Initialize (3 times)
FinalizeArray (which calls Finalize 3 times)

This will allow me to write much faster code using records and operator overloading, since I can now tune the "Initialize" and "Finalize" functions to be much faster than the default handlers. The CopyRecord call is also avoided, since the Data field is no longer reference counted.

If the "initialize" function can not be implemented due to technical issues (e.g. when the record is used as a field of an object, when is the record initialize function called?), then if records can optionally be zero-initialized (not just the reference counted fields in them), then that will already be a useful improvement.
Steps to Reproduce:
N/A - Enhancement request

Ivan Levashew at 12/17/2006 1:40:22 AM -
Look here :

What about follow this way?
Or use Ada and be relaxed about what's still wrong in Delphi.

Delphi's and Ada's ways of finalizing are different a bit.
While Delphi holds the RTTI of each variable used in a function,
GNAT's approach is to add each new Controlled object to a
local finalization queue.

Please, support the QC 37791 (Ada/Delphi) proposal.

Gabriel Corneanu at 6/23/2009 4:36:53 AM -
I also need this, but for a different reason.
I want to use records where I initialize some simple fields (integer).
It looks like a default (parameterless) constructor (which is currently NOT allowed).

Eugene Balabuev at 3/29/2010 6:00:58 AM -
First, by design, Delphi variables are zero initialized. It will be hard to change this behavior. But! Even zero initialization of some (specially marked) fields will be good enough, because it will allow to use these fields as flags.

Second, again by design, Delphi are free to move variables in memory without calling any internal routines. This happens when dynamic arrays are resized, or, for example, in TList<T> container.

However, I see no problem to provide custom Finalize/Copy operators.
This become much more interesting now, because Delphi's records becomes more powerful. Combining this feature with private fields/methods/properties can lead to many interesting innovations.

I'm professional component developer. Currently, I have to use private interface reference as a workaround, but this is not always efficient enough, and this is not real custom Copy.

wang rui at 7/17/2011 12:26:24 AM -
It looks like a default (parameterless) constructor (which is currently NOT allowed),too.

I have try to implement like "TString" or "TBigInt",but the Record not have  default "constructor/initialize"/"destructor/Finalize" method.
It've been complicated.

Ivan Levashew at 9/12/2012 4:35:48 AM -
OK, many years passed, but the problem is still here. It can be workarounded in several ways. Not a single way will make Delphi close to Ada and C++, but at least it will be comfortable to use your custom types.

First trick is to put interface or custom variant inside private field of record.

If you care about Delphi 7 compatibility (we still do), here comes the second trick: use old-style "object" from Borland Pascal with Objects on Delphi 6, 7, 2005. Detect compiler version and use conditional compilation to produce record on newer compilers and objects on older compilers. Delphi 7 will warn about unsafe type, but this is the only inconvenience in Delphi 7. Old-style object behave in many ways similar to record. It is copied automatically like a record and it can have private fields, public methods and properties.

Don't use destructors, they are not allowed in Delphi 2006 records. Argumentless constructors are also not allowed even if you don't name them "Create". Both old-style objects and new-style records have class functions, but they can't be invoked outside of old-style object method implementations. Generally, test every feature on both versions of Delphi.

Finalization happens on behalf of private interface or custom variant field. Custom variant takes more space, but it can sometimes be one indirection layer less than something wrapped inside interfaced object. Consider both options.

The record (or object) in question provides all the methods and functions and proxies them to the inner object. Every proxy can auto-initialize, auto-clone inner object.

If you use custom variant, you can control what happens when your variant is being copied. If you use interface, you can implement copy-on-write: when data needs to be modified, query interface refcount (interface must provide RefCount property) and optionally clone it before proceeding to the next operation.

There is no initialization. Every record method must be able to deal with nil interface reference and varEmpty Variant. Private field must be initialized on demand before forwarding a call to a field.

Having resolved every issue you'll get good-looking custom type and all the uglyness will be hidden deep inside.

Bruno Fratini at 4/9/2013 1:24:37 AM -
I think having such kind of improvement is something like a must with the new record capabilities.

Records now are quite more widely used than before due to the fact the developer do not have to instanciate and free them and also because they have operator overload something that is still missing for classes.

The lack of custom initialize, finalize and copy for managing internal dynamic data as buffers, objects, interfaces and so on really limitate records flexibility and generate some development issue.

Please add those features.

Leif Uneus at 6/4/2013 4:36:36 AM -
When adding class operators on records for the initialization/finalization part, consider adding class operators for all intrinsic functions, like Length,SetLength,Copy,High,Low etc. See QC116074 "Class Operator Length missing",

Alexey Kazantsev at 4/20/2014 9:04:11 AM -
Please, vote for QC#124048 report (class operator Copy: too

wang rui at 9/3/2014 8:55:29 PM -
Part of the implemented of this function.

Server Response from: ETNACODE01