views:

347

answers:

4

Given a text string containing a type name, is there some way to get the appropriate type itself?

I'm looking to do something like this:

type
  TSomeType<T> = class
    // yadda yadda
  end;

procedure DoSomething;
var
  obj : TObject;
begin
  o := TSomeType<GetTypeByName('integer')>.Create;
  // do stuff with obj
end;

I've looked at several RTTI explanations online and looked through the Delphi units and don't see what I'm looking for. Is this possible?

+5  A: 

No, generics are entirely compiletime.

Marco van de Voort
Well, crud. Looks like you're right. That shoots down a pretty cool idea I had. Thanks for the info.
TrespassersW
Still curious if there's a way to get the type from the name, though. Even if I can't use it exactly the way I wanted.
TrespassersW
+5  A: 

You can always register your types into some sort of registry (managed by a string list or dictionary) and create a factory function to then return the appropriate object. Unfortunately you would have to know in advance what types you were going to need. Something similar to the Delphi functions RegisterClass and FindClass (in the classes unit). My thinking is to put the generic template type into the list directly.

An example of possible usage:

RegisterCustomType('Integer',TSomeType<Integer>);
RegisterCustomType('String',TSomeType<String>);

if FindCustomType('Integer') <> nil then
  O := FindCustomType('Integer').Create;

EDIT: Here is a specific simple implementation using a tDictionary from Generics.Collections to handle the registry storage...I'll leave extracting this into useful methods as a simple exercise for the reader.

var
  o : TObject;
begin
  TypeDict := TDictionary<String,TClass>.Create;
  TypeDict.Add('integer',TList<integer>);
  if TypeDict.ContainsKey('integer') then
    o := TypeDict.Items['integer'].Create;
  if Assigned(o) then
    ShowMessage(o.ClassName);
end;

Another EDIT: I was giving this some thought last night, and discovered another technique that you can merge into this concept. Interfaces. Here is a quick do nothing example, but can easily be extended:

TYPE
  ITest = interface
    ['{0DD03794-6713-47A0-BBE5-58F4719F494E}']
  end;

  TIntfList<t> = class(TList<T>,ITest)
  public
    function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;

procedure TForm1.Button7Click(Sender: TObject);
var
  o : TObject;
  fTestIntf : ITest;
begin
  TypeDict := TDictionary<String,TClass>.Create;
  TypeDict.Add('integer',TIntfList<integer>);
  if TypeDict.ContainsKey('integer') then
    o := TypeDict.Items['integer'].Create;
  if Assigned(o) and Supports(o,ITest,fTestIntf) then
    ShowMessage(o.ClassName);
end;

of course you would have to implement the QueryInterface, _AddRef and _Release methods and extend the interface to do something more useful.

skamradt
Thanks for this. That's pretty much exactly what I've been doing (got the idea from Delphi's TPersistent long ago). And your suggestion is what we'd come up with already (registering each template type) and that works OK. I was hoping for something more flexible and dynamic, but doesn't look like I'm going to get it. Thanks again.
TrespassersW
This can easily be made both flexible and dynamic. The only thing you can't do is handle types you didn't plan for.
skamradt
A: 

If you forget generics and basic types, the "RegisterClass" function would be helpful. But it doesn't work for generics or basic types.

Ben Ziegler
+3  A: 

The new RTTI unit in Delphi 2010 has a way of retrieving types declared in the interface section of units. For any given type, represented by a TRttiType instance, the TRttiType.QualifiedName property returns a name that can be used with TRttiContext.FindType later to retrieve the type. The qualified name is the full unit name (including namespaces, if they exist), followed by a '.', followed by the full type name (including outer types if it nested).

So, you could retrieve a representation of the Integer type (in the form of a TRttiType) with context.FindType('System.Integer').

But this mechanism can't be used to retrieve instantiations of generic types that weren't instantiated at compile time; instantiation at runtime requires runtime code generation.

Barry Kelly
Awesome! Thanks for the info.
TrespassersW