1

There are two objects: TFoo, TFoo2.

There is also a class reference : TFooClass = class of TFoo;

Both are descendants from TPersistent.

They have their own constructors:

type
  TFoo = class(TPersistent)
  private
    FC:Char;
  public
    constructor Create; virtual;
  published
    property C:Char read FC write FC;
  end;
    
  TFoo2 = class(TFoo)
  public
    constructor Create; override;
  end;

  TFooClass = class of TFoo;

...

constructor TFoo.Create;
begin
  inherited Create;
  C :=' 1';
end;
    
constructor TFoo2.Create;
begin
  inherited Create;
  C := '2';
end;

I want to create a TFoo2 object from a string, which is actually its class name : 'TFoo2'

Here is the procedure, which works fine:

procedure Conjure(AClassName:string);
var
  PClass : TPersistentClass;
  p :TPersistent;
begin
  PClass := TPersistentClass(FindClass(AClassName))
  p := TFooClass(PClass).Create;  // <-- here is called appropriate constructor  
end;

Now, I want to have similar objects like: TBobodo, TBobodo2.

And a class reference of course : TBobodoClass = class of TBobodo;

And so on...

Now, how can I pass a class reference as a parameter into a procedure, in order to secure the right constructor is called?

procedure Conjure(AClassName:string; ACLSREF: ???? ); // <-- something like that 
var
  PClass : TPersistentClass;
  p :TPersistent;
begin
  PClass := TPersistentClass(FindClass(AClassName))
  p := ACLSREF(PClass).Create;  // <-- something like that  
end;

Is it possible?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
lyborko
  • 2,571
  • 3
  • 26
  • 54
  • Derive all your classes from a base class with a virtual constructor, and you don't need to pass a class reference – David Heffernan Oct 12 '21 at 15:33
  • Yes, but I would like to have it this way in order to create "low hierarchy" objects like TFont, TPen ect.... just wondering, if there is some cute solution – lyborko Oct 12 '21 at 15:39
  • I guess it depends on whether or not you want a solution that works – David Heffernan Oct 12 '21 at 16:12
  • @DavidHeffernan I have routines, which save and load TPersistent objects (and all descendands) into XML. The only weak point is the one procedure, where I have to explicitly use class references to create Classes, what looks ugly. – lyborko Oct 12 '21 at 17:34
  • Maybe you can use an approach like `RegisterClass`, i.e. create a mapping of base class name to metaclass reference. – Uli Gerhardt Oct 13 '21 at 08:18
  • @UliGerhardt even if you did that, you still can't use a metaclass *via a variable* to call a virtual constructor at runtime. The compiler needs to know what the metaclass is at compile-time. In which case, I would probably instead map each type name to a function that returns an instance of that type, and then call those functions when needed. – Remy Lebeau Oct 13 '21 at 16:46
  • Does this answer your question? [Is there a way to instantiate a class by its name in delphi?](https://stackoverflow.com/questions/701049/is-there-a-way-to-instantiate-a-class-by-its-name-in-delphi) – SilverWarior Oct 14 '21 at 01:34

2 Answers2

3

There is no way to do what you want in Delphi 7. The metaclass reference has to be explicit at compile-time at the call site, not handled at runtime.

In Delphi 2009 and later, you may 1 be able to do something with Generics, eg:

1: I have not tried this myself yet.

type
  TConjureHelper = class
  public
    class procedure Conjure<TClassType>(const AClassName: string);
  end;

class procedure TConjureHelper.Conjure<TClassType>(const AClassName: string);
var
  PClass : TPersistentClass;
  p : TPersistent;
begin
  PClass := TPersistentClass(FindClass(AClassName));
  p := TClassType(PClass).Create;
  ...
end;

...

TConjureHelper.Conjure<TFooClass>('TFoo2');
TConjureHelper.Conjure<TBobodoClass>('TBobodo2');
...

But Delphi 7 certainly does not support Generics.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I do not have Delphi 9, but your answer helps me to look for another solution for my problem. Thanx.... – lyborko Oct 13 '21 at 14:45
0

I had the same problem and after some struggles, I found a quite simple solution: metaclass is invented exactly for this purpose! In your case, you can pass the metaclass as parameter and use it directly without the cumbersome finding class and type casting.

type
  TFooClass = class of TFoo;

procedure Conjure(aFooClass : TFooClass); // <-- something like that 
var
  p :TPersistent;
begin
  p := aFooClass.Create;  // it will work!
end;

and by calling, you simply use:

Conjure(TFoo); // <- for Foo class or
Conjure(TFoo2); // <- for Foo2 class and so on
Sinuhe69
  • 1
  • 2
  • I tested my solution on FPC thus I think on Delphi 7 it must work, too. If you just interest in how you can create an instance from a class name then you can follow this thread. The mentioned method using virtual constructor will IMO **not** work. (Tested on FPC, the compiler will generate code for the constructor of the base class, regardless it was declared as virtual or not) – Sinuhe69 Nov 04 '21 at 15:03
  • https://stackoverflow.com/questions/5772755/delphi-create-class-from-a-string – Sinuhe69 Nov 04 '21 at 15:15