2010-07-07 4 views
5

devo fare un po 'record di calcolo delle dimensioni sub-dati in modo creato qualcosa di simileCome si ottiene l'indirizzo di un campo se si tratta di una procedura o di un puntatore a funzione?

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

Tutto è ok tranne per un caso, se uno di campo è una procedura o funzione puntatore, quindi in caso di

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

La funzione ottiene l'indirizzo della procedura stessa. C'è un modo per digitare il campo per ottenere l'indirizzo del campo e non l'indirizzo della funzione? So che posso risolverlo con i record delle varianti delle varianti, ma preferisco non usarlo.

Grazie,

Max

risposta

11

Hai provato @@?

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

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

mi dà 8, che è quello che ci si aspetterebbe su Delphi 2010.

N @

+6

+1 - Dai documenti (Tipi procedurali): "Per ottenere l'indirizzo di memoria di una variabile procedurale (piuttosto che l'indirizzo memorizzato in esso), usa @ @ ". –

+0

Grazie, Nat, sembra che questa sia una buona domanda per testare le abilità Delphi, ti sembra di passare facilmente;) – Maksee

+0

@Maksee: Grazie! Non direi che le mie abilità sono migliori di altre (specialmente Mason!), Mi è capitato di dover risolvere un problema simile anch'io una volta. :) – Nat

1

Purtroppo, ci non sembra essere un modo semplice per fare questo il modo in cui si sta facendo. Probabilmente i record delle parti varianti funzionerebbero, ma sono d'accordo, è piuttosto brutto.

Ma se sei su Delphi 2010, c'è un modo migliore. Sembra che tu stia cercando di esaminare tutti i campi del record e determinare i loro offset. Questo può essere fatto con esteso RTTI, in questo modo:

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; 

(Il writeln presuppone che si trovi in ​​una console app scritto puramente a scopo dimostrativo, come ho scritto fino a testare questo Sentitevi liberi di modificarlo per adattarlo. le vostre esigenze ...)

3

Come il vostro Campo2 è un puntatore metodo, si dovrebbe fare uso di tale:

Questo codice

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; 

mettere in OnCreate di una forma con un memo o n esso, produce:

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

La seconda linea mostra un valore casuale, come nulla è stato assegnato alla variabile record locale. La terza e la quarta riga mostrano gli indirizzi dei membri TMethod.

Si noti che è possibile (può essere) compilare il record a causa del fatto che i metodi sembrano essere sempre allineati a 8 byte. (Almeno in D2009/D2010).

+0

> "..filling nel record .." - @Marjan, lo prendo te' ho provato con un, diciamo, 'Word' Field1 e un' packed' record? Non c'è allineamento con D2007 .. –

+0

@Sertac: No, mi dispiace. Ho individuato i byte aggiuntivi nell'esempio che ho dato e noto per esperienza passata che D2010 e D2009 sembrano allineare i puntatori del metodo su limiti di 8 byte. Rilevato in una classe che deve essere una copia esatta (field wise) di un'altra classe che ha una definizione di record annidata. Abbiamo un controllo del tempo di esecuzione sulla dimensione dell'istanza per rilevare le modifiche involontarie a una, ma non l'altra. Questo controllo potrebbe talvolta riuscire e talvolta fallire, sempre senza modifiche a nessuna classe ma con modifiche ad altro codice. Mettere {$ A8} nella nostra copia ha risolto questo problema. –

+0

Sono un po 'confuso; AFAIK '{$ A8}' è già l'allineamento predefinito.Quindi un 'Byte' o un 'TMethod' si posizionerebbero su un limite di 8 byte per un record non compresso per impostazione predefinita. Tuttavia grazie per la spiegazione, penso di aver in qualche modo frainteso qualcosa .. –