views:

192

answers:

2

I need determine if a TRttiMethod is a function

so far, i wrote this function

Function IsFunction(QualifiedName,MethodName:string):Boolean;
Var
  ctx     : TRttiContext;
  lType   : TRttiType;
  lMethod : TRttiMethod;
Begin
    result:=false;
    ctx := TRttiContext.Create;
    lType:=ctx.FindType(QualifiedName);
    if Assigned(lType) then
    begin
       lMethod:=lType.GetMethod(MethodName);
         if Assigned(lMethod) then
           Result:=(lMethod.ReturnType<>nil); //in this line the exception is raised
    end;
End;

but this function fail with this exception Insufficient RTTI available to support this operation. when i test the Following

IsFunction('SysUtils.Exception','CreateFmt')

also fails with these classes and methods

SysUtils.Exception -> CreateFmt
SysUtils.Exception -> CreateResFmt
SysUtils.Exception -> CreateResFmt
SysUtils.Exception -> CreateFmtHelp
SysUtils.Exception -> CreateResFmtHelp
SysUtils.Exception -> CreateResFmtHelp
SysUtils.TEncoding -> Convert
SysUtils.TEncoding -> Convert
SysUtils.TEncoding -> GetBufferEncoding
SysUtils.TEncoding -> GetBufferEncoding
SysUtils.TEncoding -> GetByteCount
SysUtils.TEncoding -> GetByteCount
SysUtils.TEncoding -> GetByteCount
SysUtils.TEncoding -> GetByteCount
SysUtils.TEncoding -> GetBytes
SysUtils.TEncoding -> GetBytes
SysUtils.TEncoding -> GetBytes
SysUtils.TEncoding -> GetBytes
SysUtils.TEncoding -> GetBytes
SysUtils.TEncoding -> GetCharCount
SysUtils.TEncoding -> GetCharCount
SysUtils.TEncoding -> GetChars
SysUtils.TEncoding -> GetChars
SysUtils.TEncoding -> GetChars
SysUtils.TEncoding -> GetPreamble
SysUtils.TEncoding -> GetString
SysUtils.TEncoding -> GetString
SysUtils.TMBCSEncoding -> GetPreamble
SysUtils.TUTF8Encoding -> GetPreamble
SysUtils.TUnicodeEncoding -> GetPreamble
SysUtils.TBigEndianUnicodeEncoding -> GetPreamble
Rtti.TRttiMethod -> Invoke
Rtti.TRttiMethod -> Invoke
Rtti.TRttiMethod -> Invoke
Generics.Collections.TList<Rtti.TMethodImplementation.TParamLoc> -> AddRange
Generics.Collections.TList<Rtti.TMethodImplementation.TParamLoc> -> AddRange
Generics.Collections.TList<Rtti.TMethodImplementation.TParamLoc> -> AddRange
Generics.Collections.TList<Rtti.TMethodImplementation.TParamLoc> -> InsertRange
Generics.Collections.TList<Rtti.TMethodImplementation.TParamLoc> -> InsertRange
Generics.Collections.TList<Rtti.TMethodImplementation.TParamLoc> -> InsertRange
Generics.Collections.TArray -> Sort
Generics.Collections.TArray -> Sort
Generics.Collections.TArray -> Sort
Generics.Collections.TArray -> BinarySearch
Generics.Collections.TArray -> BinarySearch
Generics.Collections.TArray -> BinarySearch

i wrote this small app to check this behavior

program ProjectTest;

{$APPTYPE CONSOLE}

uses
  Rtti,
  SysUtils;

Function IsFunction(QualifiedName,MethodName:string):Boolean;
Var
  ctx     : TRttiContext;
  lType   : TRttiType;
  lMethod : TRttiMethod;
Begin
    result:=false;
    ctx := TRttiContext.Create;
    lType:=ctx.FindType(QualifiedName);
    if Assigned(lType) then
    begin
       lMethod:=lType.GetMethod(MethodName);
       try
         if Assigned(lMethod) then
           Result:=(lMethod.ReturnType<>nil);
       except on e : exception do
           Writeln(Format('%s %s -> %s',[e.Message,QualifiedName,MethodName]));
       end;
    end;
End;


var
  ctx     : TRttiContext;
  lType   : TRttiType;
  lMethod : TRttiMethod;
begin
  try
    ctx := TRttiContext.Create;
    for lType in  ctx.GetTypes do
       for lMethod in lType.GetDeclaredMethods do
         IsFunction(lType.QualifiedName,lMethod.Name);
    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

which is the correct way to determine if an TRttiMethod is a function?

UPDATE

Thanks to the @Barry suggestions, i rewrote the function to avoid the exception, however this does not solve the problem of how to determine if a TRttiMethod is a function or not due to current limitations of the RTTI.

function IsFunction(lType : TRttiType;MethodName:string):Boolean;
Var
  ctx     : TRttiContext;
  lMethod : TRttiMethod;
Begin
    result:=false;
    if Assigned(lType) then
    begin
       lMethod:=lType.GetMethod(MethodName);
         if Assigned(lMethod) then
           if lMethod.HasExtendedInfo then
            Result:= (lMethod.MethodKind in [mkFunction,mkClassFunction]) //you can be 100 % sure, wich this is a function or not
           else // sorry but the RTTI does not provide this information when the TRttiMethod contains parameters or an resultype that are not supported by the RTTI
             Result:=False;
    end;
End;
A: 

Your IsFunction function should work fine in most cases. But some functions don't have RTTI generated for them, because their parameters include types that don't have RTTI available, such as TBytes or array of const. But for most functions, your function will work. I just hope that the omissions get filled in by the next version.

Mason Wheeler
+8  A: 

You can check TRttiMethod.HasExtendedInfo to avoid the exception. The class throws an exception for attempts to access properties for which data is only available if HasExtendedInfo returns True.

You could consider checking the MethodKind property to see if it's mkFunction or mkClassFunction, etc. as needed. MethodKind returns mkProcedure if HasExtendedInfo is False.

Barry Kelly