views:

615

answers:

3

A little example

TTest<T> = class
private
  f : T;
public
  function ToString : string;
end;

If is an object then this should work

TTest<T>.ToString;
begin
  Result := f.ToString;
end;

But what happens when is say an integer? This would be ok in .net. of course.

I know it won't work, but how do I code this to work with objects AND simple types?

+3  A: 

The last example will not work. You need to add a constraint in order to use methods. In this case TObject will be enough:

TTest<T: TObject>.ToString;
begin
  Result := T.ToString;
end;

You can use simple types with unrestrained generics, but you are very limited in the use. Because the only valid operations are assignment and comparison (equal and not equal).

In Delphi simple types are no classes, so they don't have methods. But you can do the following:

type
  TToString<T> = reference to function(const AValue: T): string;
  TGenContainer<T> = class
  private
    FValue: T;
    FToString : TToString<T>;
  public
    constructor Create(const AToString: TToString<T>);

    function ToString: string;

    property Value: T read FValue write FValue;
  end;

constructor TGenContainer<T>.Create(const AToString: TToString<T>);
begin
  FToString := AToString;
end;

function TGenContainer<T>.ToString: string;
begin
  Result := FToString(FValue);
end;



procedure TForm2.Button1Click(Sender: TObject);
var
  gen : TGenContainer<Integer>;
begin
  gen := TGenContainer<Integer>.Create(
    function(const AValue: Integer): string
    begin
      Result := IntToStr(AValue);
    end);
  try
    gen.Value := 17;
    Memo1.Lines.Add(gen.ToString);
  finally
    gen.Free;
  end;
end;

This works fine.

Gamecat
yes I know that, but how do I get it to work. It works perfectly in .net because everything is an object.
Steve
A: 

I guess I could wrap my simple types in objects, and override the ToString function, but it does defeat the purpose of generics.

Steve
Yup, simple types do not have methods.
Gamecat
+5  A: 

There are three reasons why Delphi doesn't let you do what you are trying to do in your second example - call the ToString method on a value of an unconstrained type parameter type (or at least that's what I think you were trying to show, since TObject.ToString is an instance method, not a class method, so T.ToString would not work even for TObject).

  1. Delphi does not have a rooted type system, and there are very few operations that are common to all types. These operations - copying, assignment, creating locations (fields, locals, parameters, arrays) - are the only operations that are guaranteed to be available on all possible values of the type parameter.

  2. Following on from 1, why are the operations restricted to these? Why not permit the operations in the generic class, and only give errors at instantiation time? Well, the first part of the reason is that the design was originally intended to maximize compatibility with .NET and dccil, so things that .NET generics did not permit did not have significant affordances in the Win32 generics design.

  3. The second justification for the design is that check-only-at-instantiation-time is problematic. The most famous parametric polymorphism implementation that uses this approach is C++ templates, and it is also famous for its cryptic error messages, as you e.g. try to pass the wrong kind of iterator to an algorithm, and get odd complaints about overloaded operators not being found. The deeper the polymorphism, the worse the problem. In fact, it's so bad that C++ is itself rectifying this fault in the form of C++ 0x Concepts.

Hopefully you now understand why operations that aren't guaranteed to be available via constraints cannot be used. However, you can get out of this restriction relatively easily, by providing the operation in the form of a method reference or interface implementation, as Gamecat suggested.

In the future, generics in Delphi for Win32 will likely be extended along similar lines to C++ 0x Concepts, or Haskell type classes, such that type parameters may be constrained to have certain methods, functions and operators available. If it went along type class lines, then type inference may proceed in that fashion too.

Barry Kelly