org - Cómo obtener el nombre del procedimiento/función actual en Delphi(como una cadena)
mapwindows 5 (6)
Creo que este es un duplicado de esta pregunta: ¿Cómo obtener el nombre del método actual en Delphi 7?
La respuesta es que para hacerlo, necesita algún tipo de información de depuración en su proyecto, y para usar, por ejemplo, las funciones de JCL para extraer información de él.
Añadiré que no he usado el nuevo soporte RTTI en D2009 / 2010, pero no me sorprendería si hubiera algo inteligente que pudieras hacer con él. Por ejemplo, esto le muestra cómo enumerar todos los métodos de una clase , y cada método está representado por un TRttiMethod . Eso desciende de TRttiNamedObject que tiene una propiedad Name que "especifica el nombre de la entidad reflejada" . Estoy seguro de que debe haber una manera de obtener una referencia de dónde se encuentra actualmente, es decir, el método en el que se encuentra actualmente. Esto es todo una suposición, ¡pero trate de probarlo!
¿Es posible obtener el nombre del procedimiento / función actual como una cadena, dentro de un procedimiento / función? Supongo que habría algún "macro" que se expande en tiempo de compilación.
Mi situación es la siguiente: tengo muchos procedimientos que tienen un registro y todos deben comenzar por verificar la validez del registro, por lo que pasan el registro a un "procedimiento de validación". El procedimiento de validación (el mismo para todos los procedimientos) genera una excepción si el registro no es válido, y quiero que el mensaje de la excepción no incluya el nombre del procedimiento del validador, sino el nombre de la función / procedimiento que llamó al validador procedimiento (naturalmente).
Es decir, tengo
procedure ValidateStruct(const Struct: TMyStruct; const Sender: string);
begin
if <StructIsInvalid> then
raise Exception.Create(Sender + '': Structure is invalid.'');
end;
y entonces
procedure SomeProc1(const Struct: TMyStruct);
begin
ValidateStruct(Struct, ''SomeProc1'');
...
end;
...
procedure SomeProcN(const Struct: TMyStruct);
begin
ValidateStruct(Struct, ''SomeProcN'');
...
end;
Sería algo menos propenso a errores si en cambio pudiera escribir algo así como
procedure SomeProc1(const Struct: TMyStruct);
begin
ValidateStruct(Struct, {$PROCNAME});
...
end;
...
procedure SomeProcN(const Struct: TMyStruct);
begin
ValidateStruct(Struct, {$PROCNAME});
...
end;
y luego, cada vez que el compilador encuentra {$ PROCNAME}, simplemente reemplaza la "macro" con el nombre de la función / procedimiento actual como un literal de cadena.
Actualizar
El problema con el primer enfoque es que es propenso a errores. Por ejemplo, sucede fácilmente que te equivocas al copiar y pegar:
procedure SomeProc3(const Struct: TMyStruct);
begin
ValidateStruct(Struct, ''SomeProc1'');
...
end;
o errores tipográficos
procedure SomeProc3(const Struct: TMyStruct);
begin
ValidateStruct(Struct, ''SoemProc3'');
...
end;
o simplemente confusión temporal:
procedure SomeProc3(const Struct: TMyStruct);
begin
ValidateStruct(Struct, ''SameProc3'');
...
end;
Creo que lo está haciendo a la inversa: primero, compruebe si hay un error y solo entonces (es decir: necesita el nombre de la persona que llama) use alguna herramienta como JclDebug para obtener el nombre de la persona que llama pasando la declaración dirección de la pila a ella.
Obtener el nombre del procedimiento es muy costoso en cuanto al rendimiento, por lo que solo quiere hacerlo cuando sea absolutamente necesario.
Estamos haciendo algo similar y solo confiamos en una convención: poner un const SMethodName
sosteniendo el nombre de la función desde el principio .
Entonces todas nuestras rutinas siguen la misma plantilla , y usamos esta const en Assert y otras excepciones.
Debido a la proximidad de la const con el nombre de rutina, hay pocas posibilidades de que un error tipográfico o cualquier discrepancia permanezcan allí por mucho tiempo.
YMMV por supuesto ...
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;
He resuelto problemas similares a través del diseño. Tu ejemplo me confunde porque parece que ya estás haciendo esto.
Envuelve sus funciones de validación una vez como esta:
procedure SomeValidateProc3(const Struct: TMyStruct);
begin
ValidateStruct(Struct, ''SomeProc3'');
end;
Luego, en lugar de llamar repetidamente:
ValidateStruct(Struct, ''SomeProc3");
Llama:
SomeValidateProc3(Struct);
Si tienes un error tipográfico, el compilador lo detectará:
SoemValidateProc3(Struct);
Si usa un nombre más significativo para sus funciones de contenedor como "ValidateName", el código también se vuelve más legible.
No hay macro de tiempo de compilación, pero si incluye suficiente información de depuración, puede usar la pila de llamadas para averiguarlo. Mira esta misma pregunta .
Otra forma de lograr el efecto es ingresar los metadatos fuente en un comentario especial como
ValidateStruct(Struct, ''Blah''); // LOCAL_FUNCTION_NAME
Y luego ejecute una herramienta de terceros sobre su fuente en un evento de compilación de precompilación para buscar líneas con "LOCAL_FUNCTION_NAME" en dicho comentario, y reemplace todos los literales de cadena con el nombre del método en el que aparece dicho código, de modo que, por ejemplo, el código se convierte
ValidateStruct(Struct, ''SomeProc3''); // LOCAL_FUNCTION_NAME
si la línea de código está dentro del método "SomeProc3". No sería difícil escribir una herramienta como esta en Python, por ejemplo, y esta sustitución de texto hecha en Delphi también sería bastante fácil.
Tener la sustitución realizada automáticamente significa que nunca tendrá que preocuparse por la sincronización. Por ejemplo, puede usar herramientas de refactorización para cambiar los nombres de sus métodos, y luego sus literales de cadena se actualizarán automáticamente en el siguiente pase de compilación.
Algo así como un preprocesador de fuente personalizado.
Le di a esta pregunta un +1, esta es una situación que he tenido muchas veces antes, especialmente para los mensajes por fallas de afirmación. Sé que el seguimiento de la pila contiene los datos, pero tener el nombre de rutina dentro del mensaje de afirmación hace las cosas un poco más fáciles, y hacerlo manualmente crea el peligro de mensajes obsoletos, como señaló el OP.
EDITAR : Los métodos JcdDebug.pas
como se destacan en otras respuestas parecen ser mucho más simples que mi respuesta, siempre que la información de depuración esté presente.