Действительно ли возможно получить название текущей процедуры/функции как строка, в рамках процедуры/функции? Я предполагаю, что был бы некоторый "макрос", который расширен во время компиляции.
Мой сценарий - это: у Меня есть много процедур, которым дают запись, и они все должны запустить путем проверки законности записи, и таким образом, они передают запись на "процедуру блока проверки допустимости". Процедура блока проверки допустимости (тот же для всех процедур) повышает исключение, если запись недопустима, и я хочу, чтобы сообщение исключения включало не название процедуры блока проверки допустимости, но название функции/процедуры который названный процедурой блока проверки допустимости (естественно).
Таким образом, я имею
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;
Мы делаем нечто подобное и полагаемся только на соглашение: помещаем 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;
Я думаю, что это дубликат этого вопроса: Как получить имя текущего метода в 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, тоже была бы достаточно простой.
Автоматическое выполнение подстановки означает, что вам никогда не придется беспокоиться о синхронизации. Например, вы можете использовать инструменты рефакторинга для изменения имен методов, и тогда ваши строковые литералы будут автоматически обновлены при следующем проходе компилятора.
Что-то вроде пользовательского препроцессора исходных текстов.
Я поставил этому вопросу +1, это ситуация, с которой я сталкивался уже много раз, особенно для сообщений о сбоях утверждений. Я знаю, что трассировка стека содержит данные, но наличие имени рутины внутри сообщения об утверждении немного упрощает ситуацию, а если делать это вручную, то возникает опасность устаревших сообщений, как указал ОП.
EDIT: Методы JcdDebug.pas
, выделенные в других ответах, кажутся гораздо более простыми, чем мой ответ, при условии, что информация об отладке присутствует.
Я решал подобные проблемы с помощью дизайна. Ваш пример сбивает меня с толку, потому что вы, похоже, уже делаете это.
Вы обертываете свои функции валидации один раз вот так:
procedure SomeValidateProc3(const Struct: TMyStruct);
begin
ValidateStruct(Struct, 'SomeProc3');
end;
Затем вместо многократного вызова:
ValidateStruct(Struct, 'SomeProc3");
Вы вызываете:
SomeValidateProc3(Struct);
Если у вас есть опечатка, компилятор поймает ее:
SoemValidateProc3(Struct);
Если вы используете более осмысленное имя для своих функций-оберток, например "ValidateName", код также становится более читабельным.
Я думаю, что вы делаете это неправильно: Сначала проверьте, есть ли ошибка, и только потом (то есть: вам нужно имя вызывающей процедуры) используйте какой-нибудь инструмент типа JclDebug, чтобы получить имя вызывающей процедуры, передав ей адрес возврата из стека.
Получение имени процедуры очень затратно с точки зрения производительности, поэтому вы хотите делать это только в случае крайней необходимости.