Подтвердить что ты не робот

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

Обновление

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

  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;
4b9b3361

Ответ 1

Мы делаем что-то подобное и полагаемся только на соглашение: ставим 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;

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

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

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

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

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

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

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

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

Ответ 5

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

Вы завершаете свои функции проверки следующим образом:

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

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

ValidateStruct(Struct, 'SomeProc3");

Вы вызываете:

SomeValidateProc3(Struct);

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

SoemValidateProc3(Struct);

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

Ответ 6

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

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