Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the equivalent function for the CONTAINING_RECORD C macro in Delphi?

The CONTAINING_RECORD macro in C returns the base address of a structure/record type variable based on the address of a field member within the structure. It is extremely helpful in cases where I can pass only a predefined record pointer to some Windows API function which triggers callbacks.

For example if I have some types like:

type
   tInnerRecord = record
      x, y : integer;
   end;
   pInnerRecord = ^tInnerRecord

tOuterRecord = record
   field1 : integer;
   inner : tInnerRecord;
   field2 : integer;
end;
pOuterRecord = ^tOuterRecord;

I would like to be able to do something like:

procedure SomeCallback( pIn : pInnerRecord ); stdcall;
var
   Out : pOuterRecord;
begin
   Out := CONTAINING_RECORD(pIn, tOuterRecord, inner);
   Out.field1 := pIn.x + pIn.y;
end;

In my specific case I want to pass my object pointer along with the overlapped data pointer of ReadFileEx (Windows Async I/O) so I can access the object in the callback.

Is there some equivalent function that provides similar functionality in Delphi (2006)?

like image 459
blerontin Avatar asked Feb 08 '13 15:02

blerontin


1 Answers

I'm out of compiler just now, but this should do the trick:

Out := Pointer( Cardinal(pIn) - Cardinal(@TOuterRecord(nil^).inner));

David explains why there could be no direct equivalent function in Delphi. So here is a function that comes closest:

function ContainingRecord( var aField; aFieldOffsetRef : Pointer) : Pointer;
{$IF Declared(NativeUInt) = False}
type
  NativeUInt = Cardinal;
{$IFEND}
begin
  Result := Pointer(NativeUInt(@aField) - NativeUInt(aFieldOffsetRef));
end;

Calling example:

Out := ContainingRecord(pIn^, @pOuterRecord(nil).inner);
like image 133
LU RD Avatar answered Sep 29 '22 18:09

LU RD