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

CONTAINING_RECORD macro in C returns the base address of a structure / record type variable based on the address of a field member in the structure. This is extremely useful in cases where I can only pass a predefined record pointer to some Windows API function that calls callbacks.

For example, if I have types such as:

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

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

I would like 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 ReadFileEx (Windows Async I / O) data pointer overlay so that I can access the object in a callback.

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

+5
3

, :

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

, Delphi . , :

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

:

Out := ContainingRecord(pIn^, @pOuterRecord(nil).inner);
+3

, ( ). - :

procedure SomeCallback(var pIn: pInnerRecord); stdcall;
const
  p = pOuterRecord(nil);
var
  Offset: Integer;
  Out: pOuterRecord;
begin
  Offset := INT_PTR(@p^.inner) - INT_PTR(p);
  Out := Pointer(INT_PTR(@pIn) - Offset);
  Out.field1 := pIn.x + pIn.y;
end;

var
 outer: tOuterRecord;
 inner: tInnerRecord;
begin
  inner.x := 1;
  inner.y := 2;
  outer.inner := @inner;
  SomeCallback(outer.inner);
  Assert(outer.field1 = 3);
end;

. , var SomeCallback.

+3

Delphi . , , Delphi .

Obviously, in Delphi it is possible to work out an offset for a specific field in a particular record . And then it is trivial to perform the subtraction necessary to obtain the record containing the base address. But what you cannot do is express a one-time calculation and reuse it for any common pair of fields / records, as you can do with the C macro.

You will need to write one function for each pair of fields / records that you are working with.

+1
source

All Articles