Records and objects

Records can be evil (slow hidden copy, record hidden slow cleanup code, a fillchar would make any string in record become a memory leak...), but they are sometimes very convenient to access a binary structure (e.g. some "smallish value"), via a pointer.

If using pointers, you must know what you are doing. It's definitively more preferable to stick with classes, and TPersistent if you want to use the magical "VCL component ownership model". 
I only try to use records if there is no reference counted fields in them.

But sometimes, I still use records to use some algorithms on the stack, or as part of a class internal data storage: no need to a try..finally block for the stack use, or no need to a Create/Free (or TPersistent.Create) for the class use.

Here comes the problem

There are some problems with using record or object with recent versions of Delphi, in comparison to the "regular" usage from Turbo Pascal 5.5 up to Delphi 2007.

Some "enhancements" (mostly marketing, IMHO) were introduced to the record model since Delphi 2009: the object keyword was told as "deprecated", you could add methods to a record, and some properties 

But existing code won't compile any more. Just try to compile a packed object structure in Delphi 2009/2010/XE... and you'll find out that it's not possible anymore. You'll have to either define a packed record either use the {$A-} conditional define.
The problem is that a packed record can't have methods with older Delphi version. So if, like me, you are writing libraries and want them to compile to most Delphi IDE version (not only the last one), you can't use that. So I've used the {$A-} conditional define in my source code.

Those past days, I just found out another issue.

Normally, if you define a record on the stack, containing some reference-counted variables (like a string or a dynamic array), it will be initialized by some compiler magic code, at the begin and end level of the method/function:

type TObj = object Int: integer; Str: string; end;
procedure Test;
  var O: TObj
begin // here, an _InitializeRecord(@O,TypeInfo(TObj)) call is made
  O.Str := 'test';
  (...)
end;  // here, a _FinalizeRecord(@O,TypeInfo(TObj)) call is made

Those _InitializeRecord and _FinalizeRecord will "prepare" then "release" the O.Str variable.

With Delphi 2010, I found out that sometimes, this _InitializeRecord() and _FinalizeRecord() calls were not always generated.
If the record has only some no public fields, the hidden calls are sometimes not generated by the compiler.
Just build the source again, and there will be...

The only solution I found out was using the record keyword instead of object.

So here is how my code looks like:

/// used to store and retrieve Words in a sorted array
// - is defined either as an object either as a record, due to a bug
// in Delphi 2010 compiler (at least): this structure is not initialized
// if defined as a record on the stack, but will be as an object
TSortedWordArray = {$ifdef UNICODE}record{$else}object{$endif}
public
  Values: TWordDynArray;
  Count: integer;
  /// add a value into the sorted array
  // - return the index of the new inserted value into the Values[] array
  // - return -(foundindex+1) if this value is already in the Values[] array
  function Add(aValue: Word): PtrInt;
  /// return the index if the supplied value in the Values[] array
  // - return -1 if not found
  function IndexOf(aValue: Word): PtrInt; {$ifdef HASINLINE}inline;{$endif}
end;

The {$ifdef UNICODE}record{$else}object{$endif} is awful... but the code generation error didn't occur since..
The resulting modifications in the source code are not huge, but a bit disappointing.

What's next?

Perhaps you'll find, and you'll be perfectly right, that I should get rid on the record/object types here, and use plain Delphi classes.
And, in order to get rid of the Free call, use an interface.
But, defining an interface just to add some basic garbage collection without any object-oriented need... it's not a perfect solution either, even if can be handy in some cases...

I'm perhaps "border-line", playing with the compiler fire...

But it's just a pity that EMB doesn't have, in their automated test suite, some tests dedicated to the record/object part of the language, in order to avoid such regressions.

Feedback and comments are welcome on our forum.