Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 507
norgepaul Avatar asked Mar 18 '15 09:03

norgepaul


1 Answers

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;
like image 151
Stefan Glienke Avatar answered Oct 28 '22 11:10

Stefan Glienke