Comment obtenir le nom de la procédure/fonction actuelle dans Delphi (sous forme de chaîne de caractères))

est-il possible d'obtenir le nom de la procédure/fonction actuelle en tant que chaîne, dans le cadre d'une procédure/fonction? Je suppose qu'il y aurait une "macro" qui serait développée au moment de la compilation.

mon scénario est celui-ci: j'ai beaucoup de procédures qui sont données un dossier et ils ont tous besoin de commencer par vérifier la validité du dossier, et donc ils passent le dossier à une"procédure de validateur". La procédure de validation (la même pour toutes les procédures) soulève une exception si l'enregistrement est invalide, et je veux que le message de l'exception n'inclue pas le nom de la procédure de validateur, mais le nom de la fonction/procédure qui a appelé la procédure de validateur (naturellement).

C'est-à-dire que j'ai

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

puis

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

...

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

ce serait un peu moins sujet aux erreurs si je pouvais écrire quelque chose comme

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

...

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

et puis à chaque fois le compilateur rencontre un {$PROCNAME}, il remplace simplement la "macro" par le nom de la fonction/procédure courante comme une chaîne littérale.

mise à Jour

Le problème avec la première approche est qu'elle est sujette à erreur. Par exemple, il arrive facilement que vous vous trompez, en raison de copier-coller:

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

ou typos:

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

ou tout simplement la confusion temporaire:

procedure SomeProc3(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, 'SameProc3');
  ...
end;
22
demandé sur Facundo Casco 2010-05-12 13:59:58

6 réponses

nous faisons quelque chose de similaire et nous nous en remettons uniquement à une convention: mettre un const SMethodName contenant le nom de la fonction au tout début .

Puis toutes nos routines suivent le même modèle , et nous utilisons cette const dans Assert et autre levée D'Exception.

En raison de la proximité de la const avec le nom de routine, il ya peu de chances qu'une faute de frappe ou une divergence resterait là pour long.

YMMV de cours...

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;
10
répondu François 2010-05-12 17:40:19

je pense que c'est une copie de cette question: Comment obtenir le nom de la méthode actuelle dans Delphi 7?

la réponse est que pour ce faire, vous avez besoin d'une certaine forme de debug info dans votre projet, et d'utiliser, par exemple, les fonctions JCL pour en extraire des informations.

j'ajouterai que je n'ai pas utilisé le nouveau support RTTI en D2009/2010, mais cela ne me surprendrait pas s'il y avait quelque chose d'intelligent vous pourrait faire avec elle. Par exemple , cela vous montre comment liste Toutes les méthodes d'une classe , et chaque méthode est représentée par un TRttiMethod . Cela descend de TRttiNamedObject qui a un propriété Nom qui" spécifie le nom de l'entité reflétée" . Je suis sûr qu'il doit y avoir un moyen d'obtenir une référence à l'endroit où vous êtes actuellement, c'est à dire la méthode que vous vivez actuellement. C'est toutes les conjectures, mais essayez de lui donner un aller!

8
répondu David M 2017-05-23 12:18:10

pas de macro de compilation time, mais si vous incluez suffisamment d'informations de débogage, vous pouvez utiliser la callstack pour le trouver. Voir la même question .

2
répondu Lars Truijens 2017-05-23 11:54:56

une autre façon d'obtenir l'effet est d'entrer les métadonnées source dans un commentaire spécial comme

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

et ensuite lancer un outil tiers sur votre source dans un événement de pré-compilation pour trouver des lignes avec "LOCAL_FUNCTION_NAME" dans un tel commentaire, et remplacer toutes les lignes littérales avec le nom de la méthode dans laquelle ce code apparaît, de sorte que par exemple le code devient

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

si la ligne de code se trouve à l'intérieur de la méthode" SomeProc3". Il serait pas difficile du tout d'écrire un tel outil en Python, par exemple, et cette substitution de texte faite en Delphi serait assez facile aussi.

avoir la substitution faite automatiquement signifie que vous n'avez jamais à vous soucier de la synchronisation. Par exemple, vous pouvez utiliser des outils de recadrage pour changer le nom de vos méthodes, et ensuite vos caractères alphabétiques seront automatiquement mis à jour sur le prochain passage du compilateur.

quelque chose comme un pré-processeur source personnalisé.

j'ai donné cette question a +1, c'est une situation que j'ai eu plusieurs fois auparavant, surtout pour des messages pour des échecs d'assertion. Je sais que la trace de la pile contient les données, mais avoir le nom de la routine à l'intérieur du message d'assertion rend les choses un peu plus faciles, et le faire manuellement crée le danger de messages périmés, comme L'OP l'a souligné.

EDIT : les méthodes JcdDebug.pas comme souligné dans d'autres réponses semblent être beaucoup plus simple que ma réponse, à condition que les informations de débogage est présent.

2
répondu Caleb Hattingh 2010-05-12 13:00:58

j'ai résolu des problèmes similaires par la conception. Votre exemple me trouble parce que vous semblez déjà le faire.

vous enveloppez vos fonctions de validation une fois comme ceci:

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

puis au lieu d'appeler à plusieurs reprises:

ValidateStruct(Struct, 'SomeProc3");

vous appelez:

SomeValidateProc3(Struct);

si vous avez une faute de frappe, le compilateur l'attrapera:

SoemValidateProc3(Struct);

si vous utilisez un nom pour vos fonctions de wrapper comme "ValidateName", le code devient aussi plus lisible.

0
répondu Marcus Adams 2010-05-12 14:03:16

je pense que vous faites le mauvais sens: Tout d'abord, vérifiez s'il y a une erreur et seulement alors (c'est-à-dire que vous avez besoin du nom de l'appelant) utilisez un outil comme JclDebug pour obtenir le nom de l'appelant en lui passant l'adresse de retour de la pile.

obtenir le nom de la procédure est très coûteux de performance sage, donc vous voulez seulement le faire quand absolument nécessaire.

0
répondu dummzeuch 2010-05-12 20:50:23