Problems passing a pointer to a constructor as a parameter - delphi

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));

Related

Spring4D - How to resolve from container with TComponent parameter to auto factory

For an example I would like to resolve a class passing in a TComponent and TNotifyEvent such as below but the base constructor of TObject gets called and not of TMy.
GlobalContainer.RegisterType<TMy>;
GlobalContainer.RegisterFactory<Func<TComponent,TNotifyEvent,TMy>>(TParamResolution.ByType);
var F:=GlobalContainer.Resolve<Func<TComponent,TNotifyEvent,TMy>>;
F(Self,Self.OnActivate);
I can get around the issue by writing some very ugly code as below but think that this kind of resolution would be so common that I must be doing something wrong.
TOther = class
end;
TMy = class
public
constructor Create(C: TComponent; N: TNotifyEvent; O: TOther);
end;
procedure TForm2.Button1Click(Sender: TObject);
begin
GlobalContainer.RegisterType<TOther>;
GlobalContainer.RegisterType<TMy>;
GlobalContainer.RegisterType<Func<TComponent,TNotifyEvent,TMy>>(
function: Func<TComponent,TNotifyEvent,TMy>
begin
Result:=Func<TComponent,TNotifyEvent,TMy>(
function(O: TComponent; N: TNotifyEVent): TMy
begin
Result:=TMy.Create(O,N,GlobalContainer.Resolve<TOther>);
end
);
end
);
GlobalContainer.Build;
var F:=GlobalContainer.Resolve<Func<TComponent,TNotifyEvent,TMy>>;
F(Self,Self.OnActivate);
end;
constructor TMy.Create(C: TComponent; N: TNotifyEvent; O: TOther);
begin
OutputDebugString('Resolved');
end;
Thanks in advance for any pointers.
This should work now after the latest commit in develop.
The issue was the typed parameter resolution was bound to the argument type instead of the parameter type. In this case, this resulted in a typed value with type TForm2 (taken from the argument being passed) which did not match the type of the C argument of the constructor of type TComponent because that matching checks for type identity and not assignment compatibility.
After the fix typed parameter resolution works exactly on the parameter types of the factory function instead of the possibly (in case of objects) more narrow actual type of the argument.
FWIW for future reference - when registering a factory manually it's usually not necessary to use RegisterType providing the delegate but can directly use RegisterInstance like so (keep in mind that Spring.Func<...> has const` parameters) when it does not have any captured state:
GlobalContainer.RegisterInstance<Func<TComponent,TNotifyEvent,TMy>>(
function(const O: TComponent; const N: TNotifyEVent): TMy
begin
// ....
end);
Edit: I also added automatic detection for the best parameter resolution default. When the factory type is a Spring.Func<...> it automatically uses ByType so it can omitted from the RegisterFactory call. For all other types it uses ByName as default as before.

Adding an overload in child class

I have a base class TParent which defines a method without the overload directive:
TParent = class
public
procedure Test();
end;
In a child class TChild, I'm trying to add an overload for the Test procedure.
TChild = class(TParent)
public
procedure Test(AParam : Integer);
end;
On compiling there are no errors neither warnings but if I try to use the TParent.Test with a TChild instance, it gives an E2035 error, like if the parent method was hidden by the child's one:
var
Obj : TChild;
begin
Obj := TChild.Create;
try
Obj.Test();
finally
Obj.Free;
end;
end;
[dcc32 Error] Unit1.pas(52): E2035 Not enough actual parameters
In order to resolve the compiling error, I have to add the overload directive to the TChild.Test declaration.
TChild = class(TParent)
public
procedure Test(AParam : Integer); overload;
end;
It compiles and seems to work, but is it correct even if the TParent.Test declaration has no overload directive at all? Or should I change the name of the child's procedure in case the parent class has not predicted the method to be overloaded?
I believe this is perfectly OK.
The documentation on overloading methods (as opposed to simply overloading non-method procedures and functions) states
A method can be redeclared [in a descendant class (my remark)] using the overload directive. In this case, if the redeclared method has a different parameter signature from its ancestor, it overloads the inherited method without hiding it. Calling the method in a descendent class activates whichever implementation matches the parameters in the call.
This is the only description given, and it isn't crystal clear. Still, the most reasonable interpretation is that it is perfectly fine to do what you suggest. The fact that it works and is used in a lot of existing Delphi source code I think settles it.

how to call inherited constructor of TObjectDictionary in Delphi

I created the following class, after reading about the significant performance improvement of TDictionary over TStringList:
TAnsiStringList = class(TObjectDictionary<AnsiString,TObject>)
public
constructor Create(const OwnsObjects: Boolean = True); reintroduce;
destructor Destroy; override;
procedure Add(const AString: AnsiString);
procedure AddObject(const AString: AnsiString; AObject: TObject);
end;
I coded the constructor like this:
{ TAnsiStringList }
constructor TAnsiStringList.Create(const OwnsObjects: Boolean = True);
begin
if OwnsObjects then
inherited Create([doOwnsKeys,doOwnsValues])
else
inherited Create;
end;
...expecting that this TObjectDictionary constructor would be called:
constructor Create(Ownerships: TDictionaryOwnerships; ACapacity: Integer = 0); overload;
...if the Ownerships parameter were specified. If the Ownerships parameter is not specified, I expected that the following inherited TDictionary constructor would be called:
constructor Create(ACapacity: Integer = 0); overload;
The code compiles and runs, but when I call
inherited Create([doOwnsKeys,doOwnsValues]) I get the following error:
Invalid class typecast
Does anyone see what I'm doing wrong, and is there a proper way to do this?
TIA
The problem is that you are asking the container to call Free on your keys when items are removed. But your keys are not classes and so that is an invalid request. This is trapped at runtime rather than compile time because the ownership is not determined until runtime.
You only want doOwnsValues and should remove doOwnsKeys.
if OwnsObjects then
inherited Create([doOwnsValues])
else
inherited Create;
For what it is worth, if you are trying to maked an AnsiString equivalent to TStringList, then your approach is flawed. A string list is an ordered container, but a dictionary is not. You won't be able to index by integer, iterate in order and so on. I also do not understand why you would want to force all consumers of this class to declare the objects to be of type TObject. You should leave that parameter free for the consumer to specify. That's the beauty of generics.
Perhaps you don't want an ordered container, in which case a dictionary is what you need. But in that case you simply don't need to create a new class. You can simply use TObjectDictionary as is.
If you are dead set on creating a sub class then I'd do it like this:
type
TAnsiStringDict<T: class> = class(TObjectDictionary<AnsiString, T>)
That will allow the consumer of the class to decide which type of objects they put in the dictionary, and maintain compile time type safety.
So, when you want a dictionary of list boxes your declare a variable like this:
var
ListBoxDict: TAnsiStringDict<TListBox>;

Delphi XE: Can I call virtual constructors with parameters from a classtype-constrained generic type without resigning to hacks?

I'm trying to build a generic ancestor for composite controls. The initial idea looked something like this:
type
TCompositeControl<TControl1: TControl; TControl2: TControl> = class(TWinControl)
private
FControl1,
FControl2: TControl;
public
constructor Create(AOwner: TComponent); override;
end;
TLabelAndEdit = TCompositeControl<TLabel, TEdit>; // simple example for illustration only
constructor TCompositeControl<TControl1,TControl2>.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FControl1 := TControl1.Create(Self);
FControl2 := TControl2.Create(Self);
end;
As you might already be aware, this will trigger compiler error E2568: Can't create new instance without CONSTRUCTOR constraint in type parameter declaration. Adding the constructor constraint doesn't help however as it implies a parameter-less constructor.
Casting the templates to TControl makes the code compilable:
...
FControl1 := TControl(TControl1).Create(Self);
...
...but it results in an Access Violation at runtime.
One hack that would probably work is invoking the constructor via RTTI, but I would consider that a rather dirty solution.
Another hack that essentially works is using class type variables as intermediates:
type
TControlClass = class of TControl;
constructor TCompositeControl<TControl1,TControl2>.Create(AOwner: TComponent);
var
lCtrlClass1,
lCtrlClass2: TControlClass;
begin
inherited Create(AOwner);
lCtrlClass1 := TControl1;
FControl1 := lCtrlClass1.Create(Self);
lCtrlClass2 := TControl2;
FControl2 := lCtrlClass2.Create(Self);
end;
Is there a cleaner solution? Also, can somebody explain to me why the classtype-constraint does not suffice for invoking the virtual constructor on the type parameter directly?
Your typecast is bad: TControl(TControl1).Create(Self). That tells the compiler that TControl1 is an instance of TControl, but we know that it's not an instance. It's a class reference. Type-cast it to the class-reference type instead:
FControl1 := TControlClass(TControl1).Create(Self);
An alternate syntax is
FControl1 := TControl1(TControl1.NewInstance); // get memory for object
FControl1.Create(self); // call type-specific constructor
FControl2 := TControl2(TControl2.NewInstance); // get memory for object
FControl2.Create(self); // call type-specific constructor
This is used in Delphi's Classes.pas::CreateComponent
I just can't decide which option is the least ugly!
It seems it the latest delphi version (Seattle) this compiler error is not emitted anymore. I had the same issue we an application, but only when compiled with DelphiXe8 not with delphi Seattle
If your Class uses a constructor without parameters (like TObject), I would suggest to do what the compiler says:
"Add a constructor constraint to the type-parameter declaration"
It should look something like this:
TCompositeControl < Control1: TControl, constructor; TControl2: TControl, constructor > = Class(TWinControl)
If you do so, you should be able to make the necessary call to the constructor of the generic Type.
Howether, I don't know, if it works with a constructor, who needs a parameter.
Please let us know, if it works.

Delphi pointer syntax

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);

Resources