Как Получить название Текущей Процедуры/Функции в 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;
25
задан Facundo Casco 5 May 2012 в 20:45
поделиться

6 ответов

Мы делаем нечто подобное и полагаемся только на соглашение: помещаем const SMethodName , содержащее имя функции в самом начале .
Затем все наши подпрограммы следуют одному и тому же шаблону , и мы используем эту константу в Assert и других исключениях.
Из-за близости константы к имени подпрограммы маловероятно, что опечатка или какое-либо несоответствие останется там надолго.
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;
10
ответ дан 28 November 2019 в 21:52
поделиться

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

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

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

8
ответ дан 28 November 2019 в 21:52
поделиться

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

2
ответ дан 28 November 2019 в 21:52
поделиться

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

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

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

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

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

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

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

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

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

2
ответ дан 28 November 2019 в 21:52
поделиться

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

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

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

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

ValidateStruct(Struct, 'SomeProc3");

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

SomeValidateProc3(Struct);

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

SoemValidateProc3(Struct);

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

0
ответ дан 28 November 2019 в 21:52
поделиться

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

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

0
ответ дан 28 November 2019 в 21:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: