Domanda

È possibile ottenere il nome della procedura/funzione corrente come stringa, all'interno di una procedura/funzione?Suppongo che ci sarebbe qualche "macro" che viene espanso in fase di compilazione.

Il mio scenario è questo:Ho molte procedure a cui viene assegnato un record e tutte devono iniziare verificando la validità del record, quindi passano il record a una "procedura di convalida".La procedura di validazione (la stessa per tutte le procedure) solleva un'eccezione se il record non è valido, e voglio che il messaggio dell'eccezione includa non il nome della procedura di validazione, ma il nome della funzione/procedura che ha chiamato il validatore procedura (naturalmente).

Cioè, l'ho fatto

procedure ValidateStruct(const Struct: TMyStruct; const Sender: string);
begin
 if <StructIsInvalid> then
    raise Exception.Create(Sender + ': Structure is invalid.');
end;

poi

procedure SomeProc1(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, 'SomeProc1');
  ...
end;

...

procedure SomeProcN(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, 'SomeProcN');
  ...
end;

Sarebbe un po' meno soggetto a errori se potessi invece scrivere qualcosa del genere

procedure SomeProc1(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, {$PROCNAME});
  ...
end;

...

procedure SomeProcN(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, {$PROCNAME});
  ...
end;

e quindi ogni volta che il compilatore incontra un {$PROCNAME}, sostituisce semplicemente la "macro" con il nome della funzione/procedura corrente come stringa letterale.

Aggiornamento

Il problema con il primo approccio è che è soggetto a errori.Ad esempio, capita facilmente di sbagliare a causa del copia-incolla:

  procedure SomeProc3(const Struct: TMyStruct);
  begin
    ValidateStruct(Struct, 'SomeProc1');
    ...
  end;

o errori di battitura:

procedure SomeProc3(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, 'SoemProc3');
  ...
end;

o solo confusione temporanea:

procedure SomeProc3(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, 'SameProc3');
  ...
end;
È stato utile?

Soluzione

Stiamo facendo qualcosa di simile, e solo contare su una convenzione: mettere un const SMethodName tenendo il nome della funzione proprio all'inizio
. Poi tutte le nostre routine seguono lo stesso modello , e usiamo questo const in affermare e altri innalzamento delle eccezioni.
A causa della vicinanza del const con il nome di routine, ci sono poche possibilità di un errore di battitura o di qualsiasi discrepanza sarebbe rimasto lì a lungo.
YMMV naturalmente ...

procedure SomeProc1(const Struct: TMyStruct);
const
  SMethodName = 'SomeProc1';
begin
  ValidateStruct(Struct, SMethodName);
  ...
end;

...

procedure SomeProcN(const Struct: TMyStruct);
const
  SMethodName = 'SomeProcN';
begin
  ValidateStruct(Struct, SMethodName);
  ...
end;

Altri suggerimenti

Credo che questo è un duplicato di questa domanda: Come per ottenere il nome del metodo corrente in Delphi 7

?

La risposta è che per fare in modo, avete bisogno di qualche forma di informazioni di debug nel progetto, e per l'uso, ad esempio, il JCL funzioni per estrarre informazioni da esso.

io aggiungere che non ho usato il nuovo supporto RTTI nel D2009 / 2010, ma non mi sorprenderebbe se ci fosse qualcosa di intelligente che si possa fare con esso. Per esempio, questo mostra come lista tutti i metodi di una classe , e ogni metodo è rappresentato da un TRttiMethod . Che scende dal TRttiNamedObject che ha una proprietà nome che "specifica il nome dell'entità riflessa" . Sono sicuro che ci deve essere un modo per ottenere un riferimento al punto in cui ci si trova, vale a dire il metodo che si è attualmente in. Questo è tutto congetture, ma provare a dare che un andare!

Non c'è tempo di compilazione macro, ma se si includono sufficienti informazioni di debug è possibile utilizzare lo stack di chiamate per scoprirlo. Vedere questa stessa domanda .

Un altro modo per ottenere l'effetto è inserire i metadati di origine in un commento speciale come

ValidateStruct(Struct, 'Blah'); // LOCAL_FUNCTION_NAME

Quindi esegui uno strumento di terze parti sul tuo sorgente in un evento di build precompilato per trovare le righe con "LOCAL_FUNCTION_NAME" in tale commento e sostituire tutti i valori letterali stringa con il nome del metodo in cui appare tale codice, in modo che ad es.il codice diventa

ValidateStruct(Struct, 'SomeProc3'); // LOCAL_FUNCTION_NAME

se la riga di codice si trova all'interno del metodo "SomeProc3".Non sarebbe affatto difficile scrivere uno strumento del genere in Python, ad esempio, e anche questa sostituzione del testo fatta in Delphi sarebbe abbastanza semplice.

Avere la sostituzione eseguita automaticamente significa che non dovrai mai preoccuparti della sincronizzazione.Ad esempio, puoi utilizzare gli strumenti di refactoring per modificare i nomi dei metodi e quindi i valori letterali stringa verranno aggiornati automaticamente al passaggio successivo del compilatore.

Qualcosa come un preprocessore di origine personalizzato.

Ho dato a questa domanda un +1, questa è una situazione che ho già avuto numerose volte, soprattutto per i messaggi relativi agli errori di asserzione.So che l'analisi dello stack contiene i dati, ma avere il nome della routine all'interno del messaggio di asserzione rende le cose un po' più semplici e farlo manualmente crea il pericolo di messaggi obsoleti, come sottolineato dall'OP.

MODIFICARE:IL JcdDebug.pas i metodi evidenziati in altre risposte sembrano essere molto più semplici della mia risposta, a condizione che siano presenti informazioni di debug.

Ho risolto problemi simili attraverso il design. Il vostro esempio mi confonde, perché ti sembra di essere già facendo questo.

È avvolgere la funzioni di validazione, una volta in questo modo:

procedure SomeValidateProc3(const Struct: TMyStruct);
  begin
    ValidateStruct(Struct, 'SomeProc3');
  end;

Poi, invece di chiamare più volte:

ValidateStruct(Struct, 'SomeProc3");

Si chiama:

SomeValidateProc3(Struct);

Se si dispone di un errore di battitura, il compilatore catturarlo:

SoemValidateProc3(Struct);

Se si utilizza un nome più significativo per le funzioni wrapper come "ValidateName", il codice diventa più leggibile anche.

Penso che si sta facendo il giro modo sbagliato: In primo luogo, verificare se v'è un errore e solo allora (cioè: È necessario il nome del chiamante). Utilizzare qualche strumento come JclDebug per ottenere il nome del chiamante passando l'indirizzo di ritorno dalla pila ad esso

Come il nome della procedura è la prestazione molto costoso saggio, così desideri solo farlo quando è assolutamente necessario.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top