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

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

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

это я

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}, он просто заменяет "макрос" именем текущая функция / процедура в виде строкового литерала.

обновление

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

  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 ответов


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

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;

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

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

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


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


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

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

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

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

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

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

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

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

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


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

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

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

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

ValidateStruct(Struct, 'SomeProc3");

звоните:

SomeValidateProc3(Struct);

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

SoemValidateProc3(Struct);

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


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

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