Function passed as parameter causes an E2035 error - delphi

In one file, I have a piece of code that goes like this:
// foo.pas
unit Foo;
type
FooExecutorMethod = function (param: Pointer) : Integer of Object;
FooExecutor = class
private
FMethod: TiTefExecutorMethod;
FMethodParam: Pointer;
FMethodResult: Integer;
public
function RunMethod(method: TiTefExecutorMethod; param: Pointer) : Integer;
end;
implementation
function FooExecutor.RunMethod(method: FooExecutorMethod; param: Pointer) : Integer;
begin
FMethod:= method; // Never gets assigned :(
FMethodParam:= param;
// Some threading logic here.
end;
In another file in the same project, I got something like this:
// bar.pas
uses Foo;
type
bar = class
protected
executor: FooExecutor;
function doSomething(params: Pointer): Integer;
Constructor Create(params: Pointer);
implementation
Function bar.doSomething(params: Pointer) : Integer;
begin
// Do something with the pointer
Result := 1;
end;
Constructor bar.Create(params: Pointer)
var
r: Integer;
begin
executor := FooExecutor.Create
r := executor.RunMethod(doSomething, params);
end;
Problem is, bar.DoSomething never gets executed. While debugging FooExecutor.RunMethod, the evaluator does not show a value for its "method" parameter - it shows the following instead:
E2035 Not enough actual parameters
And thus I am unable to use a reference to bar.DoSomething.
What am I doing wrong?

The method is never executed because you never call it. All you ever do is pass around a reference to the method. But you never call it.
You would call it like this:
function FooExecutor.RunMethod(method: FooExecutorMethod; param: Pointer): Integer;
begin
Result := method(param);
end;
Obviously your code is trying to do something else, but this is how to call the method.

Related

How to obtain GUID of interface passed as a generic parameter in Delphi?

In the following, the method Obtain() works, but the GetAs() method would be neater for callers.
However, I can't figure out how to pass an interface as a generic parameter and obtain its GUID.
Declaration:
TInterfaceRegistry = class
private
fRegistry: TDictionary<TGUID, IInterface>;
public
...
procedure Register(const IID: TGUID; IntfObj: IInterface; const Replace: Boolean = False);
function Obtain(const IID: TGUID; out IntfObj): Boolean;
function GetAs<I: IInterface>(): I;
end;
Implementation:
function TInterfaceRegistry.Obtain(const IID: TGUID; out IntfObj): Boolean;
var
Found: IInterface;
begin
Result := fRegistry.TryGetValue(IID, Found);
if Result then
if not Supports(Found, IID, IntfObj) then
raise EBatSoft.Create(SEBatSoftBug);
end;
function TInterfaceRegistry.GetAs<I>(): I;
begin
if not Obtain(I, Result) then
raise EBatSoft.Create(SEDoesNotImplement);
end;
Calling it looks like this...
IntfReg.Register(IMyIntf, TMyImpl.Create);
Getting implementation:
var
Intf: IMyIntf;
begin
// Less type-safe (can pass anything into 2nd parameter)
if IntfReg.Obtain(IMyIntf, Intf) then
...
// Fully type-safe, and simple
Intf := IntfReg.GetAs<IMyIntf>();
You have to use typeinfo via GetTypeData(TypeInfo(I)).GUID.
Keep in mind though this might return an empty guid if you did not declare any for the given interface you are using while the non generic approach simply would not compile.

Delphi 2006: I'm trying to call a function of a class, but I'm not familiar with the VAR type parameter

Here is the function I'm trying to call:
function TPipeClient.Write(var Buffer; Count: Integer): Boolean;
I have another class that is a queue of tMessages to write:
tMessage = class
public
function getData(): PByte; virtual; abstract;
function getLen(): integer; virtual; abstract;
end;
So when I try to call: TPipeClient.Write like this:
FClient.Write(queue[pos].getData(), queue[pos].getLen());
(Fclient is a tClientPipe, queue is array of tMessage)
I get an error saying no matching overlaoded version that matches, so I do a:
FClient.Write(#(queue[pos].getData()), queue[pos].getLen());
And I get another compile error: Variable required.
Any ideas on how I can cast this so it will pass and get sent out?
That is an untyped var parameter which is covered in some detail by the documentation. In short you need to pass a variable.
In your case you'll need to define one:
var
P: PByte;
And you can then call your function like this:
P := queue[pos].getData();
FClient.Write(P^, queue[pos].getLen());

Override of protected method never gets called on TObjectDispatch

I'm trying to extend a protected virtual method of TObjectDispatch. But this method never gets called.
[edited to reproduce the problem].
When I override GetPropInfo and use it in TMyDispatch it works as expected. The overrided method is called. However the overrided method on TMyDispatchItem when created by TMyDispatch (to simulate my real world example) is not called.
{$METHODINFO ON}
TExtDispatch = class(TObjectDispatch)
protected
function GetPropInfo(const AName: string; var AInstance: TObject;
var CompIndex: Integer): PPropInfo; override;
public
constructor Create;
end;
TMyDispatchItem = class(TExtDispatch)
private
FItemValue: string;
public
procedure ShowItemValue;
published
property ItemValue: string read FItemValue write FItemValue;
end;
TMyDispatch = class(TExtDispatch)
public
function GetItem: TMyDispatchItem;
private
FValue: string;
public
procedure ShowValue;
published
property Value: string read FValue write FValue;
end;
{$METHODINFO OFF}
TTestForm = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
TestForm: TTestForm;
implementation
{$R *.dfm}
procedure TTestForm.Button1Click(Sender: TObject);
var
V: Variant;
VI: Variant;
begin
V := IDispatch(TMyDispatch.Create);
V.Value := 100; //this calls inherited getpropinfo
V.ShowValue;
VI := V.GetItem;
VI.ItemValue := 5; //this doesn't
VI.ShowItemValue;
end;
{ TExtDispatch }
constructor TExtDispatch.Create;
begin
inherited Create(Self, False);
end;
function TExtDispatch.GetPropInfo(const AName: string; var AInstance: TObject;
var CompIndex: Integer): PPropInfo;
begin
Result := inherited GetPropInfo(AName, AInstance, CompIndex);
ShowMessage('GetPropInfo: ' + AName);
end;
{ TMyDispatch }
function TMyDispatch.GetItem: TMyDispatchItem;
begin
Result := TMyDispatchItem.Create;
end;
procedure TMyDispatch.ShowValue;
begin
ShowMessage('My dispatch: ' + Value);
end;
{ TMyDispatchItem }
procedure TMyDispatchItem.ShowItemValue;
begin
ShowMessage('My item value: ' + FItemValue);
end;
end.
I've actually found a way to overcome this problem by changing the datatype of TMyDispatch.GetItem to return as a Variant instead. Like this:
function TMyDispatch.GetItem: Variant;
begin
Result := IDispatch(TMyDispatchItem.Create);
end;
And now suddenly the overrided method is called. I really would like to understand what's going on here.
Any more ideas or explainations?
Virtual method dispatch in Delphi is known to work. So, if TExtDispatch.GetPropInfo is not being executed then these are the possible reasons:
The GetPropInfo method is not being called at all.
The actual instance on which GetPropInfo is being called is not an instance of TExtDispatch.
If you showed the rest of the code then we could be more sure, but the above options should be enough for you to work it out.
The only place that calls GetPropInfo is GetIDsOfNames. If your overridden GetIDsOfNames doesn't call GetPropInfo then nothing else will.
Considering your updated code, I ran it under the debugger. When the button is clicked, TObjectDispatch.GetPropInfo is called twice. The first time it is called as a result of the call to inherited GetPropInfo() in TExtDispatch.GetPropInfo. The second time it is called you can inspect ClassName to find out what class Self is. When you do that you will find that ClassName evaluates to 'TObjectDispatch'. In which case, item 2 from my list is the explanation.
I don't really understand what you are trying to do here. However, I suspect that your problem stems from the way GetItem is implemented. I suspect it should be like this:
function TMyDispatch.GetItem: IDispatch;
begin
Result := TMyDispatchItem.Create;
end;
There should have been alarm bells going off when you assigned the return value of a TInterfacedObject constructor to an object reference. That's always an error. You must assign that to an interface reference.
I expect that what happens is that the dispatch code will use an IDispatch if it encounters one, but if it finds an instance of a class instead it creates a new IDispatch to do the work. And that's the third instance of TObjectDispatch.

delphi tlist object method calling

all,
In Delphi, I created a simple class called T_Test (see below).
T_Test = class(TObject)
private
F_Int : Integer;
public
constructor Create(inInt: Integer);
destructor Destroy; override;
property Int: Integer read F_Int write F_Int;
function showInt : String;
end;
constructor T_Test.Create(inInt: Integer);
begin
F_Int := inInt;
end;
destructor T_Test.Destroy;
begin
self.Free;
end;
function T_Test.showInt : String;
var outputLine : String;
begin
result := IntToStr(Int);
outputLine := result;
Form1.Memo1.Lines.Add(outputLine);
end;
Then I have a procedure in which I want to make a TList of T_Test object and call the
showInt method function on them.
I tried like this :
procedure testTlist;
var
a, b: T_Test;
i : Integer;
begin
a := T_Test.Create(5);
b := T_Test.Create(10);
listTest := TList.Create;
listTest.Add(a);
listTest.Add(b);
listTest[i].showInt;
end;
But I keep getting an an that says I have to use a Record, Object or Class Type on the
call of 'listTest[i].showInt'
Does anyone know how to call this method ?
Cast the listTest[i] pointer back to T_Test and then call its method:
T_Test(listTest[i]).showInt;
Alternatively, if available, use a templated TObjectList class to store T_Test instances directly.
Martin's answer is correct. But it's worth noting that if you might be adding different classes to your list, a more robust fragment of code would be ...
var pMmember: pointer;
pMember := listTest[i];
if TObject( pMember) is T_Test then
T_Test( pMember).ShowInt;
Martin's point about TObjectList is quiet correct. Another option to consider would be TList<T_Test>. David's comment about the error in your destructor is correct too.
I note that you did not initialise the value of i. So the fragment above is pretending you did. If you also wanted to check that the index variable was at a valid value, and not call ShowInt if it was invalid, then you could do something like this...
if (i >= 0) and (i < listTest.Count) and (TObject(listTest[i]) is T_Test) then
T_Test(listTest[i]).ShowInt;
The above code fragment relies on short-circuit boolean evaluation.

get pointer of member function delphi

Is there some trick how to get pointer of a member function in Lazarus / delphi?
I have this code which won't compile.... Error is
in Delphi:
variable required
in Lazarus:
Error: Incompatible types: got "<procedure variable type of function(Byte):LongInt of object;StdCall>" expected "Pointer"
The code:
TClassA = class
public
function ImportantFunc(AParameter: byte): integer; stdcall;
end;
TClassB = class
public
ObjectA: TClassA;
ImportantPtr: pointer;
procedure WorkerFunc;
end;
function TClassA.ImportantFunc(AParameter: byte): integer; stdcall;
begin
// some important stuff
end;
procedure TClassB.WorkerFunc;
begin
ImportantPtr := #ObjectA.ImportantFunc; // <-- ERROR HERE
end;
Thanks!
A member function cannot be represented by a single pointer. It needs two pointers, one for the instance and one for the code. But that's implementation detail and you just need to use a method type:
type
TImportantFunc = function(AParameter: byte): integer of object; stdcall;
You can then assign ImportantFunc to a variable of this type.
Since you are using stdcall I suspect you are trying to use this as a Windows callback. That's not possible for a member function. You need a function with global scope, or a static function.
type
TImportantFunc = function(AParameter: byte): integer of object;stdcall;
ImportantPtr: TImportantFunc;
procedure TClassB.WorkerFunc;
begin
ImportantPtr := ObjectA.ImportantFunc; // <-- OK HERE
end;
ObjectA.ImportantFunc is not a memory location, so address operator # can't be applied to it - hence compiler error. It is 2 pointers, #TClassA.ImportantFunc (method code) and ObjectA (Self argument). An answer to your question depends on what you really need - code pointer, Self, both or none.
If you need just to scope a function name use static class method
TClassA = class
public
class function ImportantFunc(Instance: TClassA; AParameter: byte): integer;
stdcall; static;
end;

Resources