Delphi - Invoke Record method per name
Asked Answered
V

1

7

I wrote a scriptlanguage for my applications and my goal is to make it possible to publish any type from delphi in the script. I use rtti to automatize this task. For any instance type like classes I use the following code to find and call a method from script.

var  Info : TRttiType;  
     Meth : TRttiMethod;  
     Param : TArray<TValue>;  
     Result : TValue;  
     AnyClass : TClass;   
begin  
  ...  
  Info := RttiContext.GetType(AnyClass);  
  Meth := Info.GetMethod('AMethod');  
  Setlength(Param, 1);  
  Param[0] := TValue.From<Integer>(11);  
  Result := Meth.Invoke(ClassInstance, Param);  
  ...  
end;  

But with a record this code doesn't work, because the TRttiMethod type doesn't offer an Invoke() method for record types. I can access the method infos by Info.GetMethod('AMethod') from the record type.
For example i have a record like this:

TRecordType = record  
  Field1, Field2 : single;  
  procedure Calc(Value : integer);   
end;  

So does anyone know a way to invoke a method from a record if i have methodname or methodaddress?

Volin answered 17/4, 2012 at 13:48 Comment(8)
Did you just reinvent DWScript?Sequoia
Thanks for the hint, but I know DWScript. My language is intended as a scriptable interface to a delphi program where constructs like AObject.AFunction.AObject.DoSomething are possible.Volin
I thought DWScript could do all that, but perhaps I'm wrongSequoia
Just an answer from Barry Kelly here at SO stating that there is no RTTI for methods on records: Delphi - RTTI info about methods in records.Haswell
And here is a link to offical documentation: Information_for_Structured_Types.Haswell
Hm i can't completely agree. I can access RTTIinfos for my example record and get the name of a method (e.g. the calc method of my example record) and infos for all parameters, I also get a non zero code address. But I don't know how to invoke the method ;)Volin
Ok, RTTI for record methods was introduced in XE2. See example TRttiRecordType_(Delphi).Haswell
Not having XE2 I can't check this but, Invoke has an override that will take a TValue instead of an object. So could you do TValue.From(MyRecord) and use that in the Invoke call? (XE2 only)Gyrose
C
13

After exploring the links in delphi documentations posted in the comments above I took a closer look at the delphi type TRttiRecordMethod in System.Rtti. It provides the method DispatchInvoke() and this method expects a pointer. So following code works:

TRecordType = record   
  Field1, Field2 : single;   
  procedure Calc(Value : integer);    
end; 


  Meth : TRttiMethod; 
  Para : TRttiParameter; 
  Param : TArray<TValue>; 
  ARec : TRecordType; 
begin 
  Info := RttiContext.GetType(TypeInfo(TRecordType)); 
  Meth := Info.GetMethod('Calc'); 
  Setlength(Param, 1); 
  Param[0] := TValue.From<Integer>(12); 
  Meth.Invoke(TValue.From<Pointer>(@ARec), Param); 
end; 

If you want to call a static method or overloaded operator the code doesn't work. Delphi internally always add the self pointer to parameterlist, but this will cause a accessviolation. So use this code instead:

  Meth : TRttiMethod; 
  Para : TRttiParameter; 
  Param : TArray<TValue>; 
  ARec : TRecordType; 
begin 
  Info := RttiContext.GetType(TypeInfo(TRecordType)); 
  Meth := Info.GetMethod('&op_Addition'); 
  ... 
  Meth.Invoke(TValue.From<Pointer>(@ARec), Param); 
  Result := System.Rtti.Invoke(Meth.CodeAddress, Param, Meth.CallingConvention, Meth.ReturnType.Handle, Meth.IsStatic); 
end;    
Chaco answered 17/4, 2012 at 16:2 Comment(1)
Thanks, this really helped me in my quest for a way to dynamically call a SOAP web service!Carillon

© 2022 - 2024 — McMap. All rights reserved.