delphi record offset

Delphi: desplazamiento del campo de registro



record offset (2)

Estoy buscando formas de obtener el desplazamiento de un campo en un registro Delphi. Estos 2 métodos siguientes funcionan pero esperaba una manera más limpia. Básicamente me hubiera gustado que el tercer showmessage funcione. ¿Algunas ideas?

type rec_a=record a:longint; b:byte; c:pointer; end; {$warnings off} function get_ofs1:longint; var abc:^rec_a; begin result:=longint(@abc.c)-longint(abc); end; {$warnings on} function get_ofs2:longint; asm mov eax,offset rec_a.c end; procedure TForm1.Button1Click(Sender: TObject); begin showmessage(inttostr(get_ofs1)); showmessage(inttostr(get_ofs2)); // showmessage(inttostr(longint(addr(rec_a.c)))); // is there a way to make this one work? end;

editar: Muy bien, la respuesta a continuación funciona bien, ¡gracias! Como referencia, aquí está la salida del ensamblador para las diversas opciones:

---- result:=longint(@abc.c)-longint(abc); ---- lea edx,[eax+$08] sub edx,eax mov eax,edx ---- mov eax,offset rec_a.c ---- mov eax,$00000008 ---- result:=longint(@rec_a(nil^).c); ---- xor eax,eax add eax,$08

edit2: parece que este es un duplicado de una pregunta anterior : pregunta similar anterior como se indica a continuación por RRUZ. Como se muestra allí, otro método es declarar una variable global y usarla de la siguiente manera. Curiosamente, el compilador todavía no puede asignar el valor adecuado en tiempo de compilación como se ve en la salida del ensamblador, por lo tanto, para la eficiencia y la legibilidad, es mejor usar el método nil.

---- var ---- ---- rec_a_ofs:rec_a; ---- ---- ... ---- ---- result:=longint(@rec_a_ofs.c)-longint(@rec_a_ofs); ---- mov eax,$0045f5d8 sub eax,$0045f5d0

edit3: Ok código revisado con todas las formas de hacerlo. Tenga en cuenta que el código de ensamblador generado para los modos 3ro, 4to y 5to (método de clase) es idéntico, ya sea que estén en línea o no. ¡Elige tu forma favorita cuando hagas estas cosas!

type prec_a=^rec_a; rec_a=record a:longint; b:byte; c:pointer; class function offset_c:longint;static;inline; end; //const // rec_a_field_c_offset=longint(@rec_a(nil^).c); // no known way to make this work {$warnings off} function get_ofs1:longint;inline; var abc:^rec_a; begin result:=longint(@abc.c)-longint(abc); end; {$warnings on} function get_ofs2:longint; asm mov eax,offset rec_a.c end; function get_ofs3:longint;inline; begin result:=longint(@rec_a(nil^).c); end; function get_ofs4:longint;inline; begin result:=longint(@prec_a(nil).c); end; class function rec_a.offset_c:longint; begin result:=longint(@prec_a(nil).c); end; var rec_a_ofs:rec_a; function get_ofs6:longint;inline; begin result:=longint(@rec_a_ofs.c)-longint(@rec_a_ofs); end; procedure TForm1.Button1Click(Sender: TObject); begin showmessage(inttostr(get_ofs1)); showmessage(inttostr(get_ofs2)); showmessage(inttostr(get_ofs3)); showmessage(inttostr(get_ofs4)); showmessage(inttostr(rec_a.offset_c)); showmessage(inttostr(get_ofs6)); // showmessage(inttostr(rec_a_field_c_offset)); end;


También podría usar un enfoque genérico:

uses System.SysUtils,TypInfo,RTTI; function GetFieldOffset( ARecordTypeInfo : PTypeInfo; const ARecordFieldName : String) : Integer; var MyContext: TRttiContext; MyField: TRttiField; begin if (ARecordTypeInfo.Kind <> tkRecord) then raise Exception.Create(''Not a record type''); for MyField in MyContext.GetType(ARecordTypeInfo).GetFields do if MyField.Name = ARecordFieldName then begin Exit(MyField.Offset); end; raise Exception.Create(''No such field name:''+ARecordFieldName); end;

Y llámalo así:

ShowMessage( IntToString( GetFieldOffset( TypeInfo(rec_a),''c'')));

No tan rápido como sus otras alternativas, pero ofrece una solución genérica unificada.

Mirando sus opciones aquí para una solución limpia, parece que lo mejor es declarar una función genérica:

function GetFieldOffset( const P : Pointer) : Integer; Inline; // Example calls : // GetFieldOffset( @PMyStruct(nil).MyParameter); // GetFieldOffset( @TMyStruct(nil^).MyParameter); begin Result := Integer( P); end;

Entonces, incluso si la llamada parece incómoda, el nombre de la función le dice lo que está sucediendo. Al inicializar la llamada, se elimina la sobrecarga de la llamada de función, por lo que funcionará como un embellecedor de código.

Es posible obtener valores constantes para una base de registro y una dirección de campo:

const cStruct : MyStruct = (); cMyInteger3Offs : Pointer = @cStruct.MyInteger3; cMyStructBase : Pointer = @cStruct;

Pero esto no hará que el código se vea más limpio.


Yo siempre uso este enfoque:

Offset := Integer(@rec_a(nil^).c);

No dejes que el uso de nil^ te desanime, es perfectamente seguro. Y no se preocupe por el truncamiento del puntero de 64 bits. Si tiene un registro cuyo tamaño es> 4GB, ¡entonces tiene problemas mayores!