Как получить адрес поля, если это указатель на процедуру или функцию? - PullRequest
5 голосов
/ 07 июля 2010

Я должен сделать некоторые расчеты размера под-данных записи, чтобы создать что-то вроде

function GetSubDataSize(const Rec: TRecord): integer;
begin
  Result:=integer(@Rec.Field2) - integer(@Rec.Field1);
end;

Все в порядке, за исключением одного случая, если один из полей является указателем процедуры или функции, так что в случае

TRecord = record
   Field2: procedure(Sender: TObject) of object;
end;

Функция получает адрес самой процедуры.Есть ли способ типизировать поле, чтобы получить адрес поля, а не адрес функции?Я знаю, что могу решить эту проблему с помощью различных записей, но предпочитаю не использовать это.

Спасибо,

Макс

Ответы [ 3 ]

11 голосов
/ 07 июля 2010

Вы пробовали @@?

type
  TRecord = record
    Field1:integer;
    Field2:TNotifyEvent;
  end;

function GetSubDataSize(const Rec: TRecord): integer;
begin
  result := integer(@@Rec.Field2) - integer(@Rec.Field1);
end;

Дает мне 8, что я и ожидал от Delphi 2010.

N @

3 голосов
/ 07 июля 2010

Поскольку ваше Field2 является указателем на метод, вы должны использовать это:

Этот код

type
  RRecord = record
    Field1: Integer;
    Field2: procedure (Sender: TObject) of object;
    Field3: Integer;
  end;

var
  rec: RRecord;
begin
  Memo1.Lines.Add(Format('@rec.Field1 %d', [Integer(@rec.Field1)]));
  Memo1.Lines.Add(Format('@rec.Field2 %d', [Integer(@rec.Field2)]));
  Memo1.Lines.Add(Format('@TMethod(rec.Field2).Code %d', [Integer(@TMethod(rec.Field2).Code)]));
  Memo1.Lines.Add(Format('@TMethod(rec.Field2).Data %d', [Integer(@TMethod(rec.Field2).Data)]));
  Memo1.Lines.Add(Format('@rec.Field3 %d', [Integer(@rec.Field3)]));
end;

, помещенный в OnCreate формы с памяткой, выдает:

@rec.Field1 1244820
@rec.Field2 4052
@TMethod(rec.Field2).Code 1244828
@TMethod(rec.Field2).Data 1244832
@rec.Field3 1244836

Вторая строка показывает случайное значение, так как ничего не было назначено локальной переменной записи. Третья и четвертая строки показывают адреса членов TMethod.

Обратите внимание, что запись (возможно) заполняется из-за того, что методы, кажется, всегда выровнены на 8 байт. (По крайней мере, в D2009 / D2010).

1 голос
/ 07 июля 2010

К сожалению, кажется, нет простого способа сделать это так, как вы это делаете. Вероятно, сработали бы записи с вариантами, но я согласен, это довольно уродливо.

Но если вы на Delphi 2010, есть лучший способ. Похоже, вы пытаетесь просмотреть все поля записи и определить их смещения. Это можно сделать с помощью расширенного RTTI, например:

procedure test;
var
   ctx: TRttiContext;
   recType: TRttiType;
   recField: TRttiField;
begin
   ctx := TRttiContext.Create;
   recType := ctx.GetType(TypeInfo(TMyRec));
   for recField in recType.GetFields do
     writeln(format('Field %s is at offset %d.', [recField.Name, recField.Offset]));
end;

(Автор предполагает, что вы находитесь в консольном приложении, написанном исключительно для демонстрационных целей, как я написал для проверки этого. Не стесняйтесь изменять его в соответствии с вашими потребностями ...)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...