I have to pass an argument of the type Pointer to a function from an external DLL.
How do I create a pointer to a procedure which I can then pass to the function?
Can I also pass a pointer to a class member function to the external function, or will that not work?
Just use #MyProcedure for that.
Beware that it has to have the right calling convention (probably stdcall).
You usually can't use a member function, because it has a hidden SELF parameter.
A class static method acts like a usual procedure/function though.
http://docwiki.embarcadero.com/RADStudio/en/Methods
Create this type if procedure (or function) is method
type
TMyProc = Procedure(x:Integer;y:Integer) of Object;
or this
type
TMyProc = Procedure(x:Integer;y:Integer);
if procedure is stand alone.
Usage:
//Some class method
Procedure TfrmMain.Add(x:Integer;y:Integer);
begin
...
end;
//Another class method that uses procedure as parameter
procedure Name(proc : TMyProc);
begin
...
end;
//Call with:
Name(Add);
Related
I have an interface and I want to mock a function of this interface with an argument which is a reference to a function. See code exmple:
unit Main;
interface
procedure Execute;
implementation
uses
Spring.Mocking;
type
TRefFunc = reference to function: Boolean;
IHelper = interface
['{7950E166-1C93-47E4-8575-6B2CCEE05304}']
end;
IIntfToMock = interface
['{8D85A1CD-51E6-4135-B0E9-3E732400BA25}']
function DoSth(const AHelper: IHelper; const ARef: TRefFunc): Boolean;
end;
procedure Execute;
var
IntfMock : Mock<IIntfToMock>;
begin
IntfMock := Mock<IIntfToMock>.Create();
IntfMock.Setup.Returns(True).When.DoSth(Arg.IsAny<IHelper>, Arg.IsAny<TRefFunc>);
end;
end.
Unfortunately I receive a compile error:
[dcc32 Error] Main.pas(29): E2010 incompatible types: 'TRefFunc' and 'Spring.Mocking.Matching.TArg.IsAny<Main.TRefFunc>'
I understand why passing a callback as an argument to a mocked method is not a good idea if the method will be mocked. The best solution is to refactor the code and remove the callback argument from the method. But I was wondering if it is possible to pass an argument, which is a callback, via the Arg.IsAny<T> syntax?
Thanks and keep healthy
When passing something that is invokable to a function reference parameter the compiler tries to build a closure on it to then pass it to the parameter. This happens also for variables:
var
f: TRefFunc;
begin
f := Arg.IsAny<TRefFunc>; // boom, E2010
This is one of the few cases where Delphi needs the () on a call to understand that you actually want to invoke the rhs and assign its result.
Using Delphi 7 here. When I take the address of a procedure (with the purpose of sending this method address to an external C++ DLL as a callback) the Delphi 7 compiler reports Variable required. Why? How do you take the address of a method with or without a parameter list?
Here's my simplified code which shows the compiler error.
// ...
type
PTProcedureCallback = ^TProcedureCallback;
TProcedureCallback = procedure() of object;
// ...
TTestCallback = class
constructor Create();
procedure MyCallback();
end;
//...
implementation
constructor TTestCallback.Create();
var
pCallback: PTProcedureCallback;
begin
// Constructor
inherited;
// Test callback
pCallback := #MyCallback; // <- [Error] Variable required
end;
procedure TTestCallback.MyCallback();
begin
// Do something
end;
end;
You don't need PTProcedureCallback at all, as TProcedureCallback is already a pointer type.
constructor TTestCallback.Create();
var
pCallback: TProcedureCallback;
begin
// Constructor
inherited;
// Test callback
pCallback := MyCallback;
end;
That being said, you can't use a procedure of object as a C/C++ callback, unless the C/C++ code was written in C++Builder specifically, and is actually expecting a procedure of object via the __closure compiler extension. If not, you will not be able to use a non-static class method as the callback. However, if the callback allows you to pass in a user-defined value, you can use that to pass in your object's Self pointer so your callback can access its non-static members.
Also, your TProcedureCallback is using Delphi's default register calling convention (__fastcall in C++Builder), which does not exist in non-C++Builder compilers. Only cdecl and stdcall are portable calling conventions.
I am trying to write a basic factory method to return a generic interfaced class.
interface
type
IGenInterface<T> = interface
function TestGet:T;
end;
TBuilder<T> = class
class function Build: IGenInterface<T>;
end;
TBuilder = class
class function Build<T>: IGenInterface<T>;
end;
implementation
type
TInterfaceImpl<T> = class(TInterfacedObject, IGenInterface<T>)
function TestGet:T;
end;
{ TBuilder }
class function TBuilder.Build<T>: IGenInterface<T>;
begin
result := TInterfaceImpl<T>.create;
end;
{ TInterfaceImpl<T> }
function TInterfaceImpl<T>.TestGet: T;
begin
end;
It looks simple enough, and I'm sure I've written similar code before, but as soon as I try to compile I get E2506: Method of parameterized type declared in interface section must not use local symbol '.TInterfaceImpl` 1'. Neither flavour of TBuilder work, both failing with the same error.
Now I'm not sure where the . and 1 are coming from. In my 'real' code, the . isn't there, but the ` 1 is.
I've had a look at the other two SO questions that reference this error, but I'm not using any constants or assigning variables (other than the function return) nor do I have any class vars.
Does anyone have a way to do this without having to move a lot of code into my interface?
The issue relates to an implementation detail of generics. When you come to instantiate the generic type in a different unit it needs to see the TInterfaceImpl<T> type in that other unit. But the compiler cannot see it because it is in the implementation section of a different unit. So the compiler objects, as you have observed.
The simplest fix is to move TInterfaceImpl<T> to be a private type declared inside one of the types declared in the interface section.
type
TBuilder = class
private
type
TInterfaceImpl<T> = class(TInterfacedObject, IGenInterface<T>)
public
function TestGet: T;
end;
public
class function Build<T>: IGenInterface<T>;
end;
Or inside the other class:
type
TBuilder<T> = class
private
type
TInterfaceImpl = class(TInterfacedObject, IGenInterface<T>)
public
function TestGet: T;
end;
public
class function Build: IGenInterface<T>;
end;
I have the following superclass:
unit DlgDefaultForm;
type
TDefaultFormDlg = class(TForm)
published
constructor Create(AOwner: TComponent); reintroduce; virtual;
end;
FormCreateFunc=function(AOwner: TComponent):TDefaultFormDlg;
which is descended by a bunch of forms as follows:
unit Form1
type
TForm1 = class(TDefaultFormDlg)
published
constructor Create(AOwner: TComponent); override;
end;
and created as follows:
unit MainForm;
procedure ShowForm(FormCreate:FormCreateFunc);
begin
(do some stuff)
FormCreate(ScrollBox1);
end;
When I run
ShowForm(#TForm1.Create);
two things happen:
When I step into TForm1.Create, AOwner=nil, even when it didn't in ShowForm.
I get an EAbstractError at the following line:
unit Forms;
(...)
constructor TCustomForm.Create(AOwner: TComponent);
begin
(...)
InitializeNewForm; //EAbstractError
(...)
end;
What am I doing wrong?
EDIT: This of course isn't my exact code.
Delphi constructors take a hidden extra parameter which indicates two things: whether NewInstance needs to be called, and what the type of the implicit first parameter (Self) is. When you call a constructor from a class or class reference, you actually want to construct a new object, and the type of the Self parameter will be the actual class type. When you call a constructor from another constructor, or when you're calling the inherited constructor, then the object instance has already been created and is passed as the Self parameter. The hidden extra parameter acts as a Boolean flag which is True for allocating a new instance but False for method-style calls of the constructor.
Because of this, you can't simply store a constructor in a method pointer[1] location and expect it to work; calling the method pointer won't pass the correct value for the hidden extra parameter, and it will break. You can get around it by declaring the parameter explicitly, and doing some typecasting. But usually it is more desirable and less error-prone to use metaclasses (class references) directly.
[1] That's another problem with your code. You're trying to store a method pointer in a function pointer location. You could do that and still make it work, but you'd need to put the declaration of Self in explicitly then, and you'd also need to pass the metaclass as the first parameter when allocating (as well as passing True for the implicit flag). Method pointers bake in the first parameter and pass it automatically. To make it all explicit, the function pointer equivalent to TComponent.Create is something like:
TComponentCreate = function(Self: Pointer; AOwner: TComponent; DoAlloc: Boolean): Pointer;
Self is a pointer here because it could be of TComponentClass type, or TComponent type, depending on whether DoAlloc is true or false.
You're not using virtual constructors correctly. Try it like this:
type
TDefaultFormDlgClass = class of TDefaultFormDlg;
function Show(FormClass: TDefaultFormDlgClass; AOwner: TComponent): TDefaultFormDlg;
begin
Result := FormClass.Create(AOwner);
end;
...
var
FormClass: TTDefaultFormDlgClass;
...
FormClass := ???;//this is where you specify the class at runtime, e.g. TForm1
MyForm := Show(FormClass, MainForm);
As an aside I do not think you need to reintroduce the constructor in the code you listed.
Based on the information from Barry I tested this code. TSample is a simple class with a paramless constructor. All you need is the pointer to the constructor (#TSample.Create) and the type of the class (TSample). If you've a hashmap key=TClass, value=Pointer ctor you can create any registered type.
type
TObjectCreate = function(Self: TClass; DoAlloc: Boolean): TObject;
var
aSample : TSample;
begin
aSample := TSample(TObjectCreate(#TSample.Create)(TSample, true));
I come from a vb/c# background and I am having difficulty understanding the meaning of part of the following code, specifically the bit 'self.fColConsignments.Add'
TConsignment = class(TCollectionItem)
constructor Create(Collection : TCollection); override;
...
function TIFCSUMMsg.AddConsignment: TConsignment;
begin
result := TConsignment(self.fColConsignments.Add);
end;
if you background is C#, don't missinterpret that line:
result := TConsignment(self.fColConsignments.Add);
it's just a type cast and not a constructor call. In C# it would look like:
result = (TConsignment)self.fColConsignments.Add;
Presumably fcolConsignments is a collection owned by the TIFCSUMMsg instance (Self). Add adds a new item to the collection and returns the reference as the result. The result is then cast to a TConsignment to fit the result type of the AddConsignment method.
self.fColConsignments.Add probably adds a new item into fColConsignments, which must be a collection or similar, and returns it. But the declared return type may be more generic than the actual object returned, then a typecast is applied by using TConsignment(object).
The code in your example IS NOT A CONSTRUCTOR.
In C++/C#/Java/(put your C descendant language here), constructors are nameless methods. So:
class TFoo {
TFoo() { // do something }
}
....
{
TFoo myFoo;
myFoo = new TFoo()
.....
}
This a typical construction on such languages. This is NOT how Delphi works.
Constructors in Delphi have names. The convention is that they are called .Create and
they can be static or virtual ones (like any method).
The code above can be converted to:
TFoo = class
constructor Create();
end;
...
constructor TFoo.Create()
begin
// Do something;
end;
....
// Creating an object
var
myFoo: TFoo;
begin
myFoo := TFoo.Create();
...
end;
The code you exemplified were not an constructor but a
kind of typecast.
You can get more information about this (typecasts and constructors)
in the Delphi Language Guide (or Object Pascal Language Guide, depending
on the Delphi version you have available).