Problem with overloaded method (RTTI registered)

Hello,

I have added Rest.Client.pas as library using RTTI function. It's work fine except for Overloaded methods. There is lot of overloaded method in Delphi source...

For example AddItem for TRESTRequestParameterList class (unit REST.Client.pas)

TRESTRequestParameterList = class(TOwnedCollection)
...

    function AddItem: TRESTRequestParameter; overload;

    function AddItem(const AName, AValue: string; AKind: TRESTRequestParameterKind;
      AOptions: TRESTRequestParameterOptions = []): TRESTRequestParameter;  overload;

I use to register that class in my scripter

  Scripter.DefineClassByRTTI(TRESTRequestParameter);
  Scripter.DefineClassByRTTI(TRESTRequestParameterList);

In runtime when I try to compile such a script

var 
 RESTRequest: TRESTRequest;
...
RESTRequest.Params.AddItem;

Scripter give me error "Not enough actual parameters for method 'Additem'. Expected 6 parameters"

Does it exists a way using RTTI with DefineClassByRTTI how to handle overloaded method ?
Or how without break RTTI can I manually add an overloaded method (maybe using another name) ?
Or must I add my own method to do that like ?

Scripter.SystemLibrary.defineMethod('TRESTRequestParameter_AddItem',0,atScript.tkClass,nil,_TRESTRequestParameter_AddItem);

After the
Scripter.DefineClassByRTTI(TRESTRequestParameterList);
which doesn't seems to work with overloaded methods
I have tried to add this

  With Scripter.DefineClass(TRESTRequestParameterList) do
  begin
     DefineMethod('AddItem',4,tkClass,TJSONObject,__TRESTRequestParameterListAddItem,true,3,'');
  end;

With

procedure TatRESTClientLibrary.__TRESTRequestParameterListAddItem(AMachine: TatVirtualMachine);
  var
  AResult: variant;
  TempVar:TRESTRequestParameterOptions;
begin
  with AMachine do
  begin
   case InputArgCount  of
   0:  AResult := ObjectToVar(TRESTRequestParameterList(CurrentObject).AddItem);
   3:  AResult := ObjectToVar(TRESTRequestParameterList(CurrentObject).AddItem(AMachine.GetInputArgAsString(0), AMachine.GetInputArgAsString(1),TRESTRequestParameterKind(AMachine.GetInputArgAsInteger(2))));
   4: begin
       IntToSet(TempVar, VarToInteger(GetInputArg(3)), SizeOf(TempVar));
       AResult := ObjectToVar(TRESTRequestParameterList(CurrentObject).AddItem(AMachine.GetInputArgAsString(0), AMachine.GetInputArgAsString(1),TRESTRequestParameterKind(AMachine.GetInputArgAsInteger(2)),TempVar));
      end;
   end;

    ReturnOutputArg(AResult);
  end;
end;

But it's doesn't work because I don't understand how to declare a method with 0 or 3 or 4 parameters like TRESTRequestParameterList.addItem
I know for 3 or 4 or 5 parameters but no when 0 parameter is possible...

DefineClassByRtti indeed doesn't support overloaded methods. You have to add the methods manually like you did. That's the way to do it.
I didn't understand your statement about "0 parameter" not being possible. Scripter supports default parameters, you specify the number of default parameters like you did here:

     DefineMethod('AddItem',4,tkClass,TJSONObject,__TRESTRequestParameterListAddItem,true,3,'');

The 3 value is the number of default parameters the method accepts. Since you said the method has 4 parameters, and you can use the method without parameters, then the number of default parameters should be 4, not 3:

     DefineMethod('AddItem',4,tkClass,TJSONObject,__TRESTRequestParameterListAddItem,true,4,'');

Hello Wagner,

Prototype of TRESTRequestParameterList in (native) unit REST.Client.pas is

TRESTRequestParameterList = class(TOwnedCollection)
...

function AddItem: TRESTRequestParameter; overload;
function AddItem(const AName, AValue: string; AKind: TRESTRequestParameterKind;  AOptions: TRESTRequestParameterOptions = []): TRESTRequestParameter;  overload;
function AddItem(const AName, AValue: string): TRESTRequestParameter; overload;
function AddItem(const AName, AValue: string; const AKind: TRESTRequestParameterKind;  const AOptions: TRESTRequestParameterOptions; AContentType: TRESTContentType): TRESTRequestParameter;
function AddItem(const AName: string; const AValue: TBytes; const AKind: TRESTRequestParameterKind;  const AOptions: TRESTRequestParameterOptions; AContentType: TRESTContentType): TRESTRequestParameter;
function AddItem(const AName: string; const AValue: TStream; const AKind: TRESTRequestParameterKind;  const AOptions: TRESTRequestParameterOptions; AContentType: TRESTContentType; AOwnsStream: TRESTObjectOwnership = ooCopy): TRESTRequestParameter;

So these method can have 0, 2,4,5 or 6 arguments (parameters) in the method Additem so these can compile

TRESTRequestParameterList.Additem;
RESTRequestParameterList.Additem(MyName,MyValue);
TRESTRequestParameterList.Additem(MyName,MyValue,MyRESTRequestParameterKind);
TRESTRequestParameterList.Additem(MyName,MyValue,MyRESTRequestParameterKind,[]);

But this should not compile in scripter, Delphi say "E2250 There is no overloaded version of "AddItem" called with these argument"

TRESTRequestParameterList.Additem(MyName);
TRESTRequestParameterList.Additem(MyName,MyValue,0);

But with

 DefineMethod('AddItem',6,tkClass,TJSONObject,__TRESTRequestParameterListAddItem,true,6,'')

It's compile in scripter !

So how allow only compilation of script with 0, 2,4,5 or 6 arguments (parameters) in such a method like do Delphi ?

That is not possible. One alternative is that you raise an error at runtime if user passed 1 or 3 arguments.

Thank you for your quick answer, I have had well understood your documentation about default argument.

A last question does it a problem to do a

 Scripter.DefineClassByRTTI(TRESTRequestParameterList);

And after a

DefineMethod('AddItem',6,tkClass,TJSONObject,__TRESTRequestParameterListAddItem,true,6,'')

Or in case of Overloaded methods in class should we don't use DefineClassByRTTI registration ?

No problem, DefineMethod will replace the existing AddItem method.

Hello,

Now I have another problem with RTTI registration because of overloaded method.
So I manually add these kind of method using a "DefineMethod". It's ok.

But problem for example with :
TCustomRESTRequest.addBody(...);
We must do it also for all class which are inherited from TCustomRESTRequest.

For example TRestRequest.addBody method.

Scripter.DefineClassByRTTI(TCustomRESTRequest);
Scripter.DefineClassByRTTI(TRESTRequest);

To handle correctly overloaded AddBody method which are overloaded I must do :

  With Scripter.DefineClass(TCustomRESTRequest) do
  begin
DefineMethod('AddBody',4,tkNone,TCustomRESTRequest,__TCustomRESTRequestAddBody,true,3,'');
  end;

But I must also do it for TRESTRequest :

  With Scripter.DefineClass(TRESTRequest) do
  begin
DefineMethod('AddBody',4,tkNone,TRESTRequest,__TRESTRequestAddBody,true,3,'');
  end;

So it's a long work to do because of Overloaded method, or does it exists a quicker way ?

for each case I must study if :

  • DefineClassByRTTI
  • DefineClass
    will be easer to use.

Because it's depand on :

  • There is a lot of overloaded method
  • There is a lot of inherited classes

Well, yes, you can/should always check first if DefineClassByRTTI or DefineClass fits best your needs. Those are tools you can use.

Also note that you absolutely do not need to register methods in scripter with same name as Delphi. You can define methods like AddBody2 or whatever name you want. Or methods that don't exist in Delphi. You have full freedom.

Finally, the need to declare the same method in all classes in the hierarchy is not needed in some situations. If two classes are registered and one inherit from the other, when you call DefineMethod for the ancestor class, scripter will automatically register it in descendant classes, if there are no method with the same name declared in that class.

Hello Wagner,

Thank you for your answer,

If you do DefineMethod on ancestor and after DefineClassByRTTI on it descendant classes it seems to override all method :

 With Scripter.DefineClass(TCustomRESTRequest) do
  begin
DefineMethod('AddBody',4,tkNone,TCustomRESTRequest,__TCustomRESTRequestAddBody,true,3,'');
  end;
Scripter.DefineClassByRTTI(TRESTRequest);

So, except renaming method 'AddBody2', does the unique way to not lost overloaded method is avoid using DefineClassByRTTI like this :

 With Scripter.DefineClass(TCustomRESTRequest) do
  begin
DefineMethod('AddBody',4,tkNone,TCustomRESTRequest,__TCustomRESTRequestAddBody,true,3,'');
  end;
Scripter.DefineClass(TCustomRESTRequest);

DefineClassByRTTI accepts several parameters. One of the (ARedefine, of type TRedefineOption) specifies what it should do if a method with the same name already exist in the class.