如何获取方法指针指向的方法的名称?

时间:2014-02-21 11:31:28

标签: delphi delegates function-pointers rtti

我定义了以下内容:

  • 方法指针,如果验证正常或错误代码

    ,则返回0
    TValidationFunc = Function(AParam: TAnObject): integer Of Object;
    
  • 要执行的功能列表:

    Functions: TObjectList < TValidationFunc>;
    

我在函数列表中放了几个带有此签名的函数。

要执行它们,我执行:

For valid In Functions Do
Begin
  res := -1;
  Try
    res := valid(MyObject);
  Except
    On E: Exception Do
      Log('Error in function ??? : ' + E.Message, TNiveauLog.Error, 'PHVL');
  End;
  Result := Result And (res = 0);
End;

如果此函数引发异常,我如何在日志中获取原始函数的名称?

3 个答案:

答案 0 :(得分:4)

最简单的解决方案是 - 正如David暗示的那样 - 将名称与函数指针一起存储,如下所示:

TYPE
  TValidationFunc = Function(AParam: TAnObject): integer Of Object;
  TFunctions = TDictionary<TValidationFunc,String>;

VAR
  Functions : TFunctions;

填充列表:

Functions:=TFunctions.Create;
Functions.Add(Routine1,'Routine1');
Functions.Add(Routine2,'Routine2');

然后当你经历它时:

For valid In Functions.Keys Do Begin
  Try
    res := valid(MyObject);
  Except
    On E: Exception Do Begin
      Log('Error in function ' + Functions[valid] + ' : ' + E.Message, TNiveauLog.Error, 'PHVL');
      res := -1;
    End;
  End;
  Result := Result And (res = 0);
End;

这样,您可以将验证函数与TDictionary中的名称“链接”起来,这样当您拥有该名称时,您就可以获得另一个。

答案 1 :(得分:3)

好吧,永远不要说永远:-)。对于将其作为参数传递的事件,此函数将返回方法名称(格式为&lt; ClassType&gt;。&lt; MethodName&gt; ie.TMainForm.FormCreate)。遗憾的是,您不能使用无类型参数来允许传入任何类型的事件,但必须为您希望能够“解码”的每个方法签名编写特定例程:

FUNCTION MethodName(Event : TValidationFunc) : STRING;
  VAR
    M   : TMethod ABSOLUTE Event;
    O   : TObject;
    CTX : TRttiContext;
    TYP : TRttiType;
    RTM : TRttiMethod;
    OK  : BOOLEAN;

  BEGIN
    O:=M.Data;
    TRY
      OK:=O IS TObject;
      Result:=O.ClassName
    EXCEPT
      OK:=FALSE
    END;
    IF OK THEN BEGIN
      CTX:=TRttiContext.Create;
      TRY
        TYP:=CTX.GetType(O.ClassType);
        FOR RTM IN TYP.GetMethods DO
          IF RTM.CodeAddress=M.Code THEN
            EXIT(O.ClassName+'.'+RTM.Name)
      FINALLY
        CTX.Free
      END
    END;
    Result:=IntToHex(NativeInt(M.Code),SizeOf(NativeInt)*2)
  END;

像这样使用:

For valid In Functions Doc Begin
  res := -1;
  Try
    res := valid(MyObject);
  Except
    On E: Exception Do
      Log('Error in function '+MethodName(valid)+' : ' + E.Message, TNiveauLog.Error, 'PHVL');
  End;
  Result := Result And (res = 0);
End;

我没有使用上面的代码尝试过,但是我已经使用我的MainForm的FormCreate进行了尝试。

有一点需要注意:这只适用于生成RTTI的方法,仅适用于Delphi 2010及更高版本(它们大大增加了RTTI可用的数据量)。因此,为了确保它有效,您应该将要跟踪的方法放在PUBLISHED部分中,因为这些方法始终(默认情况下)将生成RTTI。

如果你想要它更通用一点,你可以使用这个结构:

FUNCTION MethodName(CONST M : TMethod) : STRING; OVERLOAD;
  VAR
    O   : TObject;
    CTX : TRttiContext;
    TYP : TRttiType;
    RTM : TRttiMethod;
    OK  : BOOLEAN;

  BEGIN
    O:=M.Data;
    TRY
      OK:=O IS TObject;
      Result:=O.ClassName
    EXCEPT
      OK:=FALSE
    END;
    IF OK THEN BEGIN
      CTX:=TRttiContext.Create;
      TRY
        TYP:=CTX.GetType(O.ClassType);
        FOR RTM IN TYP.GetMethods DO
          IF RTM.CodeAddress=M.Code THEN
            EXIT(O.ClassName+'.'+RTM.Name)
      FINALLY
        CTX.Free
      END
    END;
    Result:=IntToHex(NativeInt(M.Code),SizeOf(NativeInt)*2)
  END;

FUNCTION MethodName(Event : TValidationFunc) : STRING; OVERLOAD; INLINE;
  BEGIN
    Result:=MethodName(TMethod(Event))
  END;

然后你只需要为每个只调用一般实现的事件编写一个特定的MethodName,如果你把它标记为INLINE,它很可能甚至不会产生额外的函数调用,而是调用直接。

顺便说一句:我的答复很大程度上受到Cosmin Prund一年前在这个问题中给出的代码的影响:RTTI information for method pointer

如果您的Delphi没有定义NativeInt(无法记住它们何时实现),只需将其定义为:

{$IFNDEF CPUX64 }
TYPE
  NativeInt = INTEGER;
{$ENDIF }

答案 2 :(得分:1)

您可以这样做的一种方法是枚举可能的函数,寻找具有与目标匹配的地址的函数。您可以从方法指针解码实例指针。从那里你可以获得类型。然后RTTI可以完成剩下的工作。

您可以考虑在列表中存储名称和方法指针。这将是一个简单易行的方法。

我最后的想法是,您可以在项目中包含可执行地图,并在那里查看功能。例如,如果您已经使用了madExcept,EurekaLog或类似的工具,这将非常简单。

相关问题