Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use of Supports() function with generic interface type

I just tried my first use of generics in Delphi 2009 and am perplexed on how to use a generic type as the input to the Supports function used to see if an object implements a given interface. I've created a small sample illustrating the problem.

Given the following types and utility function:

IMyInterface = interface
['{60F37191-5B95-45BC-8C14-76633826889E}']
end;

TMyObject = class(TInterfacedObject, IMyInterface)
end;

class function TFunctions.GetInterface<T>(myObject: TObject): T;
var
  specificInterface: T;
begin
  // This would compile, but looses the generic capability
  //Supports(myObject, IMyInterface, specificInterface);

  // This results in compile errors
  Supports(myObject, T, specificInterface);

  result := specificInterface;
end;

and the following code snippet:

class procedure TFunctions.Test;
var
  myObject: TMyObject;
  myInterface: IMyInterface;
begin
  myObject := TMyObject.Create;

  myInterface := GetInterface<IMyInterface>(myObject);
end;

I would expect no problems but I get the following compile time errors:

[DCC Error] GenericExample.pas(37): E2029 '(' expected but ',' found [DCC Error] GenericExample.pas(37): E2014 Statement expected, but expression of type 'T' found

I'm not sure what the compiler is expecting me to do with the T when used as the actual argument to the function.

I've searched around quite a bit and haven't been able to crack this one. A part of me suspects that if I could understand how an interface name gets converted to the IID: TGUID type during compilation, when using a concrete interface name, I could make some headway, but that has evaded me also.

Any help is much appreciated.

like image 365
Chad Avatar asked Dec 11 '10 18:12

Chad


People also ask

Can we use generic in interface?

It's often useful to define interfaces either for generic collection classes, or for the generic classes that represent items in the collection. To avoid boxing and unboxing operations on value types, it's better to use generic interfaces, such as IComparable<T>, on generic classes.

What is a generic interface?

Generic Interfaces in Java are the interfaces that deal with abstract data types. Interface help in the independent manipulation of java collections from representation details. They are used to achieving multiple inheritance in java forming hierarchies. They differ from the java class.

What do you gain by using generic interface type?

What do you gain by using generic interface type? In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods.


2 Answers

There is no guarantee that T has a GUID associated with it, and there is no means in the language to write a constraint on the type parameter to make that guarantee.

The interface name is converted into a GUID by the compiler looking up the name in the symbol table, getting the compiler's data structure representing the interface, and checking the corresponding field for the GUID. But generics are not like C++ templates; they need to be compiled and type-checked and known to work for any valid type parameter, and that means constraining the type parameter in its declaration.

You can get the GUID using RTTI (first checking that T does indeed represent an interface) with something like GetTypeData(TypeInfo(T))^.Guid and pass the GUID to Supports that way.

like image 87
Barry Kelly Avatar answered Oct 04 '22 00:10

Barry Kelly


Why are you even bothering?

To use this TFunctions.GetInterface you need:

  • an interface
  • an object reference

If you have those, then you can just call Supports() directly:

  intf := TFunctions.GetInterface<IMyInterface>(myObject);

is exactly equivalent to:

  Supports(IMyInterface, myObject, intf);

Using generics here is a waste of time and effort and really begs the question "Why Do It?".

It is just making things harder to read (as is so often the case with generics) and is more cumbersome to use.

Supports() returns a convenient boolean to indicate success/failure, which you have to test for separately using your wrapper:

  intf := TFunctions.GetInterface<IMyInterface>(myObject);
  if Assigned(intf) then
    // ...

versus:

  if Supports(IMyInterface, myObject, intf) then
    // We can use intf

When creating wrappers around functionality it is generally the case that the result is an improvement in readabilty or usability.

imho this fails on both counts and you should just stick with the Supports() function itself.

like image 31
Deltics Avatar answered Oct 03 '22 23:10

Deltics