Viewed   71 times

While reviewing some code in our legacy Delphi 7 program, I noticed that everywhere there is a record it is marked with packed. This of course means that the record is stored byte-for-byte and not aligned to be faster for the CPU to access. The packing seems to have been done blindly as an attempt to outsmart the compiler or something -- basically valuing a few bytes of memory instead of faster access

An example record:

TFooTypeRec = packed record
    RID                 : Integer;
    Description         : String;
    CalcInTotalIncome   : Boolean;
    RequireAddress      : Boolean;

Should I fix this and make every record normal or "not" packed? Or with modern CPUs and memory is this negligible and probably a waste of time? Are there any problems that can result from unpacking?



There is no way to answer this question without a full understanding of how each of those packed records are used in your application code. It is the same as asking "Should I change this variable declaration from Int64 to Byte ?"

Without knowing what values that variable will be expected and required to maintain the answer could be yes. Or it could be no.

Similarly in your case. If a record needs to be packed then it should be left packed. If it does not need to be packed then there is no harm in not packing it. If you are not sure or cannot tell, then the safest course is to leave them as they are.

As a guide to making this determination (should you decide to proceed), situations where record packing is required or recommended include:

  • persistence of record values
  • sharing of record values with [potentially] differently compiled code
  • strict compatibility with externally defined structures
  • deliberately overlaying a type layout over differently structured memory

This isn't necessarily an exhaustive list, and what these all have in common is:

  • records comprising a series of values in adjacent bytes that must and can be relied upon by any potential producer or consumer of the record without possibility of interference from the compiler or other factors

What I would recommend is that (if possible and practical) you determine what purpose packing serves in each case and add documentation to that effect to the record declaration itself so that anyone in the future with the same question doesn't have to go through that discovery process, e.g.:

    TSomeRecordType = packed record
      // This record must be packed as it is used for persistence

    TSomeExternType = packed record
      // This record must be packed as it is required to be compatible
      //  in memory with an externally defined struct (ref: extern code docs)
Thursday, October 13, 2022

I had the same problem with Dymo 420P.

Try setting imageable area to the same size as paper and origin 0,0. Then after PrinterJob.printDialog(), call PrinterJob.validatePage() with your pageformat. It will return valid pageformat with proper imageable area.

Friday, December 9, 2022

You can get the offset of any record member using some pointer arithmetic:

  PMyStruct = ^MyStruct;

  Offset: Integer;
  Offset := Integer(@(PMyStruct(nil).MyInteger3));
  // or:
  // Offset := Integer(Addr(PMyStruct(nil).MyInteger3));

If you want the offset of the function you need to code it like this:

Offset := Integer(@@PMyStruct(nil).MyFunc);
// or:
// Offset := Integer(Addr(@PMyStruct(nil).MyFunc));
Wednesday, August 10, 2022

You get the warning because XE4 uses WideChar for variable of Char type (and WideString for String), so Char takes 2 bytes instead of 1 byte now. Now it is possible to keep unicode characters in String/Char, but for same reason it is impossible to use set of char anymore (in Delphi it is fixed size, 32-bytes bits map and can keep up to 256 items so).

If you use only chars from range #0..#127 (only latin/regular symbols), then you can just replace Char -> AnsiChar (but when you will assign it from Char you will see another warning, you will have to use explicit type conversion to suppress it).

If you need national/unicode symbols, then there is no "ready to use" structure in Delphi, but you can use Tdictionary for this purpose:

  TEmptyRecord = record end;

  TSet<T> = class(TDictionary<T,TEmptyRecord>)
    procedure Add(Value: T); reintroduce; inline;
    procedure AddOrSetValue(Value: T); reintroduce; inline;
    function Contains(Value: T):Boolean; reintroduce; inline;

procedure TSet<T>.Add(Value: T);
var Dummy: TEmptyRecord;
  inherited AddOrSetValue(Value, Dummy);

procedure TSet<T>.AddOrSetValue(Value: T);
var Dummy: TEmptyRecord;
  inherited AddOrSetValue(Value, Dummy);

function TSet<T>.Contains(Value: T): Boolean;
  result := inherited ContainsKey(Value);

Of course you will have initialize at as any other regular class. But it will be still quite efficient (not so fast as "set of" of course, just because "set" is always limited by 256 items max size but highly optimized).

Alternatively you can create your own set class for unicode chars as map of bits, it will take 8kb of memory to keep all the bits and will be almost as fast as "set of".

Thursday, August 4, 2022

There's nothing wrong with your keyboard at all. You've selected a keyboard layout that has dead key support on those keys. One such layout is US International, for example. Microsoft has a rather confusing table of all of the extra characters that you can type on your keyboard with these layouts. (It tells one the Unicode code points for the characters, but only hints at the actual key combinations to use to make them.)

Microsoft also provides the Microsoft Keyboard Layout Creator, for people to create their own customized layouts as they did in response to this SuperUser question, and some pictures of layouts.

Saturday, November 26, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :