views:

244

answers:

4

Hi,

related to my last question, I now have the following problem:

function TNodeFactory <T>.CreateNode (ID : Integer) : INodeInterface <T>;
var
  NodeClass : TClass;
begin
  NodeClass := FindRegisteredClass (ID);
  Result := NodeClass.Create;      
end;

This yields a compiler error:

E2010 Incompatible Types: 'INodeInterface<TNodeFactory<T>.T>' and 'TObject'

A cast does not work too.

What am I missing here?

EDIT: Current implementation

TNodeFactory <T> = class
private
  type
    TRegisteredNodeType = record
      ID : Integer;
      NodeClass : TClass;
    end;
private
  FNodeTypeList : TList <TRegisteredNodeType>
public
  procedure RegisterNodeType (ID : Integer; NodeClass : TClass);
  function  CreateNode (ID : Integer) : INodeInterface <T>;
end;

procedure TNodeFactory <T>.RegisterNodeType (ID : Integer; NodeClass : TClass);
var
  RegisteredType : TRegisteredNodeType;
begin
  RegisteredType.ID := ID;
  RegisteredType.NodeClass := NodeClass;
  FNodeTypeList.Add (RegisteredType);
end;

function TNodeFactory <T>.CreateNode (ID : Integer);
var
  RegisteredType : TRegisteredNodeType;
begin
  for RegisteredType in FNodeTypeList do
    if (RegisteredType.ID = ID) then
      Exit (RegisteredType.NodeClass.Create);
  raise EInvalidNodeType.Create ('No node type with ID ' + IntToStr (ID) + ' registered');
end;

(simplified and removed error checks)

A: 

Let me start my answer with this discloser: I'm not that familiar with the use of templates in Delphi.

You are getting the compiler error because you try to return NodeClass which is defined as an instance of TClass. However, the function expects you to return an instance of a class that implements INodeInterface. You might be able to solve this problem with something like this

Result := INodeInterface(NodeClass.Create);

or

function TNodeFactory <T>.CreateNode (ID : Integer) : INodeInterface <T>;
var
  NodeClass : TClass;
  NodeInterface: INodeInterface;
begin
  NodeClass := FindRegisteredClass (ID);
  NodeInterface:= NodeClass.Create;
  Result := NodeInterface;      
end;

This might not be the right answer but I hope this helps lead you to the solution.

Lawrence Barsanti
As I stated in the question: "a cast does not work too". And the second version is IMHO just a longer version of the first. Thanks anyway!
Smasher
A: 

You need to use the Supports() function to extract an interface reference from an object. You'll find it in SysUtils.

There are several overloaded versions, you want the three parameter version with which the third "out" parameter brings you back your interface reference.

LachlanG
Where's this method from? Can't find it in TObject or in the Delphi help...
Smasher
+2  A: 

In Delphi an Interface is not an Object. And the Interface-Pointer is not an Objectpointer, that's why you can't cast them. You need to use QueryInterface and query for the Interface.

function QueryInterface(var IID: TGUID; out Obj: Type):HRESULT

if HRESULT is S_OK, then obj contains a reference to the queried interface.

Unfortunatly Delphi (Win32) handles Interfaces different to .NET/Java.

Tobias Langner
@Smasher: If you look at the code for Supports() you'll see it calls QueryInterface() so you can use either one.
LachlanG
+1 Have not tried it, since Lieven's solution works for me. But thanks!
Smasher
+3  A: 

The problem is NodeClass being a TClass. If you create NodeClass it will be a mere TObject, not a INodeInterface.

You could try

Result := TInterfacedObject(RegisteredType.NodeClass).Create as INodeInterface

or change RegisterNodeType to

type
  TInterfacedObjectClass = class of TInterfacedObject;   
...   
procedure RegisteredNodeType...(ID: Integer; NodeClass : TInterfacedObjectClass);

and return

Result := RegisteredType.NodeClass.Create as INodeInterface.
Lieven