代码之家  ›  专栏  ›  技术社区  ›  Andreas Rejbrand

如何获取Delphi中当前过程/函数的名称(作为字符串)

  •  23
  • Andreas Rejbrand  · 技术社区  · 14 年前

    是否可以在过程/函数内以字符串形式获取当前过程/函数的名称?我想会有一些“宏”在编译时被扩展。

    我的场景是这样的:我有很多过程被赋予了一个记录,它们都需要从检查记录的有效性开始,所以它们将记录传递给一个“验证器过程”。如果记录无效,则validator过程(对所有过程都是相同的过程)会引发异常,我希望异常的消息不包括validator过程的名称,而是包含调用validator过程的函数/过程的名称(自然)。

    也就是说,我有

    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,它就会简单地用当前函数/过程的名称作为字符串文字替换“macro”。

    更新

    第一种方法的问题在于它容易出错。例如,由于复制粘贴,很容易出错:

      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 回复  |  直到 14 年前
        1
  •  10
  •   Francesca    14 年前

    我们正在做一些类似的事情,并且只依赖于一个惯例:将 康斯特 SMethodName 一开始就保存函数名 .
    然后 我们所有的程序都遵循相同的模板 ,我们在断言和其他异常引发中使用这个常量。
    由于警察和常规名称很接近,打字错误或任何差异都不会在那里停留很长时间。
    当然…

    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
  •  8
  •   Community CDub    7 年前

    我认为这是这个问题的副本: How to get current method's name in Delphi 7?

    答案是要这样做,您需要在项目中使用某种形式的调试信息,并使用 JCL 函数从中提取信息。

    我会补充说,我在D2009/2010中没有使用过新的RTTI支持,但是如果你能用它做些聪明的事情,我也不会感到惊讶。例如,这将向您展示如何 list all methods of a class ,并且每个方法都由 TRttiMethod . 从trttinamedobject派生,它具有 Name property which "specifies the name of the reflected entity" . 我相信一定有一种方法可以获得当前所在位置的引用,即您当前所在的方法。这都是猜测,但试着试试看!

        3
  •  2
  •   Community CDub    7 年前

    没有编译时宏,但是如果包含足够的调试信息,则可以使用调用堆栈来查找它。见 this same question .

        4
  •  2
  •   Caleb Hattingh    14 年前

    实现这种效果的另一种方法是将源元数据输入到一个特殊的注释中,比如

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

    然后在预编译构建事件中对源代码运行第三方工具,以在此类注释中查找带有“local_function_name”的行,并用出现此类代码的方法名称替换所有字符串文本,以便代码成为

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

    如果代码行在“someproc3”方法中。例如,用Python编写这样的工具一点也不困难,用Delphi完成的文本替换也很容易。

    自动完成替换意味着您不必担心同步。例如,您可以使用重构工具来更改方法名,然后字符串文本将在下一个编译器过程中自动更新。

    类似于自定义源预处理器。

    我给出了这个问题A+1,这是我以前多次遇到的情况,特别是对于断言失败的消息。我知道堆栈跟踪包含数据,但是断言消息中包含例程名称会使事情变得简单一点,正如OP指出的那样,手工操作会造成过时消息的危险。

    编辑 : JcdDebug.pas 如果存在调试信息,其他答案中突出显示的方法似乎比我的答案简单得多。

        5
  •  0
  •   Marcus Adams    14 年前

    我通过设计解决了类似的问题。你的例子让我困惑,因为你似乎已经在做这个了。

    将验证函数包装一次,如下所示:

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

    然后,不要重复呼叫:

    ValidateStruct(Struct, 'SomeProc3");
    

    你呼叫:

    SomeValidateProc3(Struct);
    

    如果你有错别字,编译器会捕捉到它:

    SoemValidateProc3(Struct);
    

    如果为包装函数(如“validatename”)使用一个更有意义的名称,代码也会变得更可读。

        6
  •  0
  •   dummzeuch Stijn Sanders    14 年前

    我认为你这样做是错误的: 首先,检查是否有错误,然后(即:您需要调用方的名称)使用一些工具(如jclebug)通过将返回地址从堆栈传递给调用方来获取调用方的名称。

    获取过程名是非常昂贵的性能方面的,因此您只想在绝对必要的时候进行。