Как получить имя текущей процедуры / функции в Delphi (в виде строки) - PullRequest
23 голосов
/ 12 мая 2010

Можно ли получить имя текущей процедуры / функции в виде строки внутри процедуры / функции? Я предполагаю, что будет некоторый «макрос», который раскрывается во время компиляции.

Мой сценарий таков: у меня есть много процедур, которым дается запись, и все они должны начинаться с проверки достоверности записи, и поэтому они передают запись в «процедуру проверки». Процедура валидатора (одна и та же для всех процедур) вызывает исключение, если запись недействительна, и я хочу, чтобы сообщение об исключении включало не имя процедуры валидатора, а имя функции / процедуры, вызвавшей валидатор процедура (естественно).

То есть у меня

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

, а затем

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

...

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

Было бы несколько менее подвержено ошибкам, если бы я вместо этого мог написать что-то вроде

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

...

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

и затем каждый раз, когда компилятор встречает {$ PROCNAME}, он просто заменяет «макрос» именем текущей функции / процедуры в виде строкового литерала.

Обновление

Проблема с первым подходом заключается в том, что он подвержен ошибкам. Например, бывает легко, что вы ошиблись из-за копирования-вставки:

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

или опечатки:

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

или просто временная путаница:

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

Ответы [ 6 ]

10 голосов
/ 12 мая 2010

Мы делаем что-то подобное и полагаемся только на соглашение: поместим const SMethodName с именем функции в самом начале .
Затем все наши процедуры следуют одному и тому же шаблону , и мы используем этот констант в Assert и других исключениях.
Из-за близости const с именем процедуры маловероятно, что опечатка или какое-либо несоответствие останутся там надолго.
YMMV конечно ...

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;
8 голосов
/ 12 мая 2010

Я думаю, что это дубликат этого вопроса: Как получить имя текущего метода в Delphi 7?

Ответ заключается в том, что для этого вам нужна какая-то форма отладочной информации в вашем проекте и, например, использование функций JCL для извлечения информации из него.

Я добавлю, что я не использовал новую поддержку RTTI в D2009 / 2010, но меня не удивит, если есть что-то умное, что вы могли бы с этим сделать. Например, здесь показано, как перечислить все методы класса , и каждый метод представлен TRttiMethod . Это происходит от TRttiNamedObject, который имеет свойство Name, которое «определяет имя отраженной сущности» . Я уверен, что должен быть способ получить ссылку на то, где вы находитесь в данный момент, то есть на метод, в котором вы находитесь. Это все догадки, но попробуйте попробовать!

2 голосов
/ 12 мая 2010

Еще один способ добиться эффекта - ввести метаданные источника в специальный комментарий, например

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

, а затем запустить сторонний инструмент над исходным кодом в событии предварительной компиляции, чтобы найти строки с«LOCAL_FUNCTION_NAME» в таком комментарии и замените все строковые литералы именем метода, в котором появляется такой код, чтобы, например, код становился

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

, если строка кода находится внутри метода SomeProc3.Например, совсем не сложно написать такой инструмент на Python, и такая подстановка текста в Delphi также будет достаточно простой.

Автоматическая подстановка означает, что вам никогда не придется беспокоиться о синхронизации,Например, вы можете использовать инструменты рефакторинга для изменения имен ваших методов, и тогда ваши строковые литералы будут автоматически обновляться при следующем проходе компилятора.

Что-то вроде пользовательского препроцессора исходного кода.

Я дал этому вопросу +1, это была ситуация, с которой я сталкивался много раз, особенно для сообщений о сбоях утверждений.Я знаю, что трассировка стека содержит данные, но наличие имени подпрограммы в сообщении об утверждении делает вещи немного проще, а выполнение этого вручную создает опасность устаревших сообщений, как указывал ОП.

EDIT : Методы JcdDebug.pas, как указано в других ответах, кажутся намного проще, чем мой ответ, при условии наличия отладочной информации.

2 голосов
/ 12 мая 2010

Нет макроса времени компиляции, но если вы включите достаточно отладочной информации, вы можете использовать callstack, чтобы выяснить это. Смотрите этот же вопрос .

0 голосов
/ 13 мая 2010

Я думаю, что вы делаете это неправильно: Сначала проверьте, есть ли ошибка, и только затем (то есть: вам нужно имя вызывающего) используйте какой-либо инструмент, такой как JclDebug, чтобы получить имя вызывающего, передав ему адрес возврата из стека.

Получение имени процедуры очень дорого с точки зрения производительности, поэтому вы хотите делать это только тогда, когда это абсолютно необходимо.

0 голосов
/ 12 мая 2010

Я решил подобные проблемы с помощью дизайна. Ваш пример смущает меня, потому что вы, кажется, уже делаете это.

Вы оборачиваете свои функции проверки один раз так:

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

Тогда вместо неоднократного вызова:

ValidateStruct(Struct, 'SomeProc3");

Звоните:

SomeValidateProc3(Struct);

Если у вас есть опечатка, компилятор поймает ее:

SoemValidateProc3(Struct);

Если вы используете более понятное имя для своих функций-оболочек, таких как «ValidateName», код также станет более читабельным.

...