norgepaul
norgepaul

Reputation: 6053

Using Generics to create an interfaced object

I've written a function that accepts a class type (T) and an interface type (I) and returns an interface (I) to the object (T). Here's the code.

interface

function CreateObjectInterface<T: Class, constructor; I: IInterface>(
  out AObject: TObject): I;

...

implementation

function TORM.CreateObjectInterface<T, I>(out AObject: TObject): I;
begin
  AObject := T.Create;

  if not Supports(AObject, GetTypeData(TypeInfo(I))^.Guid, Result) then
  begin
    AObject.Free;
    AObject := nil;

    raise EORMUnsupportedInterface.CreateFmt(
      'Object class "%s" does not support interface "%s"',
      [AObject.ClassName, GUIDToString(GetTypeData(TypeInfo(I))^.GUID)]
    );
  end;
end;

The function works as expected with no memory leaks or other undesirables.

Are there other ways to achieve the same result?

Upvotes: 8

Views: 601

Answers (1)

Stefan Glienke
Stefan Glienke

Reputation: 21713

There is a bug in this code. Supports will destroy your object instance if it supports IUnknown but not the interface you are asking for.

Simple demonstration:

type
  IFoo = interface
    ['{32D3BE83-61A0-4227-BA48-2376C29F5F54}']
  end;

var
  o: TObject;
  i: IFoo;
begin
  i := TORM.CreateObjectInterface<TInterfacedObject, IFoo>(o); // <- boom, invalid pointer
end.

Best to put IInterface or IUnknown as additional constraint to T.

Or make sure that you are not destroying an already destroyed instance.

Unless you want to support dynamic QueryInterface implementations (where the class does not implement the interface but QueryInterface returns it) I would go with a Supports call on the class:

function TORM.CreateObjectInterface<T, I>(out AObject: TObject): I;
begin
  if not Supports(TClass(T), GetTypeData(TypeInfo(I))^.Guid) then 
    raise EORMUnsupportedInterface.CreateFmt(
      'Object class "%s" does not support interface "%s"',
      [AObject.ClassName, GUIDToString(GetTypeData(TypeInfo(I))^.GUID)]
    );

  AObject := T.Create;
  Supports(AObject, GetTypeData(TypeInfo(I))^.Guid, Result);
end;

Upvotes: 14

Related Questions