Skip to content

[Feature] Record Composition

Frederic Kehrein requested to merge Warfley/fpcsource:recordcomposition into main

Summary

Enables a new feature Record Composition enabled via {$ModeSwitch RecordComposition}.

This feature enables to add all the members of another record, object or class to the current record, similar to the anonymous struct or unions since C11.

Example

A simplified example using a named composite field:

{$ModeSwitch RecordComposition}
 
type
  TChildRec = record
    A: Integer;
  end;
 
  TComposed = record
    uses child: TChildRec;
    B: Double;
  end;
 
var
  c: TComposed;
begin
  c.A := 42;
  c.B := 3.14;
  WriteLn(c.child.A); // Writes 42
end.

For other examples see the test cases provided by this MR

Why this feature

There are basically three reasons why I think this feature is useful:

  1. It provides extended C functionality with the anonymous structs and unions introduced in C11. This allows easy and automated conversion of C code into Pascal and to use C libraries frictionless and transparently through the same types and interfaces. The following C11 type:
struct foo {
  int x;
  struct bar; // anonymous struct inclusion of struct bar
}

Can thereby now be reperesented as

{$PackRecords C}
TFoo = record
  x: Integer;
  uses TBar;
end;

Preserving memory layout and alignment, making it transparently compatible with any C library using the C11 type definition.

  1. Allowing for non trailing and multiple variant parts. Currently variant parts can only be at the end of the record, and changing this will not be possible without major changes to the variant syntax. Using composition simply an anonymous variant record can be embedded resulting in the same effect:
TFoo = record
  x: Integer;
  uses record
    case Boolean of
    True: (I: Integer);
    False: (D: Double);
  end;
  y: Integer;
end;
  1. Avoiding {$IFDef} structures within the record definition and allowing to reuse existing types more easiely (example from https://forum.lazarus.freepascal.org/index.php/topic,64633.msg491821.html#msg491821):
// Instead of
  TRec16 = packed record
    case Integer of
      0: // unsigned word (16-bit unsigned int)
        (U16bit: Word);
      1: // pair of unsigned bytes
      {$ifdef endian_little}
        (UByteLo, UByteHi: Byte);
      {$else}
        (UByteHi, UByteLo: Byte);
      {$endif}
  end;
// By re-using existing SysUtils.WordRec
  TRec16 = packed record
    case Integer of
      0: // unsigned word (16-bit unsigned int)
        (U16bit: Word);
      1: // pair of unsigned bytes
       ( uses SysUtils.WordRec );
  end;

Merge request reports