Board index » delphi » Class Reference: what's wrong?

Class Reference: what's wrong?

Hello,

Can anybody tell me why the constructor is not called in the following code.
Very strange. The same technique works perfectly in the graphics.pas unit
for registered TGraphic descendants.

Thanks,
Andrew

----------

type
  TMyGraphic = class(TGraphic)
  public
    FField: string;
    constructor Create; override;
  end;

constructor TMyGraphic.Create;
begin
  //Please call me!!!
  inherited Create;
  FField := 'something';
  MessageBeep(1);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  GrClass: TGraphicClass;
  Graphic: TGraphic;
begin
  GrClass := TMyGraphic;
  Graphic := GrClass.Create;
  if Graphic is TMyGraphic then
    ShowMessage((Graphic as TMyGraphic).FField);
  Graphic.Free;
end;

 

Re:Class Reference: what's wrong?


On Mon, 14 Dec 1998 01:55:12 +0300, "Andrew Anoshkin"

Quote
<v...@dataforce.net> wrote:

>Hello,

>Can anybody tell me why the constructor is not called in the following code.
>Very strange. The same technique works perfectly in the graphics.pas unit
>for registered TGraphic descendants.

>Thanks,
>Andrew

>----------

>type
>  TMyGraphic = class(TGraphic)
>  public
>    FField: string;
>    constructor Create; override;
>  end;

>constructor TMyGraphic.Create;
>begin
>  //Please call me!!!
>  inherited Create;
>  FField := 'something';
>  MessageBeep(1);
>end;

>procedure TForm1.Button1Click(Sender: TObject);
>var
>  GrClass: TGraphicClass;
>  Graphic: TGraphic;
>begin
>  GrClass := TMyGraphic;
>  Graphic := GrClass.Create;
>  if Graphic is TMyGraphic then
>    ShowMessage((Graphic as TMyGraphic).FField);
>  Graphic.Free;
>end;

I can see nothing wrong with your code.

It looks like the compiler somehow ignores the virtual Create of
TGraphic for direct ('non-known') descendants. If I define

type
    TXGraphic =
        class(TGraphic)
        constructor Create; virtual;
        end;
   TXGraphicClass = class of TXGraphic;

constructor TXGraphic.Create;
begin
    inherited Create
end;

and change TGraphic into TXGraphic in your example, it works as
expected. I am tempted to conclude this may be a Delphi compiler bug.
Maybe people who know more about this kind of thing could look into it
?

hth
David

------------------
David A. Schweizer

iec ProGAMMA, The Netherlands
d.a.schweizer[OK, i don't want any more spam]gamma.rug.nl
guess where the '@' goes ?

Re:Class Reference: what's wrong?


In order for this to work:

1. Your class reference type must be cast to a class reference type
whose base class type contains a virtual constructor. Okay. TGraphic
constructor is virtual, and TGraphicClass is the right class type.
2. You must have overriden the constructor.
3. Virtual methods only get linked in, if somewhere in the code there is
either an explicit construction call, or alternatively a call to
RegisterClasses....

Have you tried putting RegisterClass(TMyGraphic) in the initialisation
section??? The Delphi code doesn't appear to do it... but I have a
feeling that there's some subtle reason why it doesn't need to...

MH.

Quote
Andrew Anoshkin wrote:

> Hello,

> Can anybody tell me why the constructor is not called in the following code.
> Very strange. The same technique works perfectly in the graphics.pas unit
> for registered TGraphic descendants.

> Thanks,
> Andrew

> ----------

> type
>   TMyGraphic = class(TGraphic)
>   public
>     FField: string;
>     constructor Create; override;
>   end;

> constructor TMyGraphic.Create;
> begin
>   //Please call me!!!
>   inherited Create;
>   FField := 'something';
>   MessageBeep(1);
> end;

> procedure TForm1.Button1Click(Sender: TObject);
> var
>   GrClass: TGraphicClass;
>   Graphic: TGraphic;
> begin
>   GrClass := TMyGraphic;
>   Graphic := GrClass.Create;
>   if Graphic is TMyGraphic then
>     ShowMessage((Graphic as TMyGraphic).FField);
>   Graphic.Free;
> end;

--
Martin Harvey.
Totally rewritten web pages at:
http://www.harvey27.demon.co.uk/mch24/

"ALGOL 60 was a language so far ahead of its time that it
was not only an improvement on its predecessors but also
on nearly all its successors". C.A.R. Hoare

--------------BEGIN GEEK CODE BLOCK--------------
Version: 3.12
GCS/CC d(+) s-:- a-- C+++$ UL@ P L@>++ E- W++
N+++ o-- K++ w+++$ O--- M-- V-- PS@ Y-- PGP-
t--- 5-- X-- R-- !tv b+ DI+ D+ G e++ h- r z++>---
---------------END GEEK CODE BLOCK---------------

Re:Class Reference: what's wrong?


In article <36758404.1FB35...@aziraphale.demon.co.uk>,
  Martin Harvey <mar...@aziraphale.demon.co.uk> wrote:

Quote
> In order for this to work:

> 1. Your class reference type must be cast to a class reference type
> whose base class type contains a virtual constructor.

     I got that far.

Quote
> Okay. TGraphic
> constructor is virtual, and TGraphicClass is the right class type.

     Yup.

Quote
> 2. You must have overriden the constructor.

     He did.

Quote
> 3. Virtual methods only get linked in, if somewhere in the code there is
> either an explicit construction call, or alternatively a call to
> RegisterClasses....

       Here you lose me. Is this the way it's supposed to work according
to the docs? I don't see anything about this under "constructors"; this
sounds like you're saying a virtual constructor can't be invoked using
a class-reference type unless you call RegisterClass first(???: is
this a guess or something you know? How do you know? I'm all confused.)

Quote
> Have you tried putting RegisterClass(TMyGraphic) in the initialisation
> section???

       Have you tried it? I tried it just now, doesn't seem to make
any difference.

Quote
> The Delphi code doesn't appear to do it... but I have a
> feeling that there's some subtle reason why it doesn't need to...

> MH.

> Andrew Anoshkin wrote:

> > Hello,

> > Can anybody tell me why the constructor is not called in the following code.
> > Very strange. The same technique works perfectly in the graphics.pas unit
> > for registered TGraphic descendants.

> > Thanks,
> > Andrew

> > ----------

> > type
> >   TMyGraphic = class(TGraphic)
> >   public
> >     FField: string;
> >     constructor Create; override;
> >   end;

> > constructor TMyGraphic.Create;
> > begin
> >   //Please call me!!!
> >   inherited Create;
> >   FField := 'something';
> >   MessageBeep(1);
> > end;

> > procedure TForm1.Button1Click(Sender: TObject);
> > var
> >   GrClass: TGraphicClass;
> >   Graphic: TGraphic;
> > begin
> >   GrClass := TMyGraphic;
> >   Graphic := GrClass.Create;
> >   if Graphic is TMyGraphic then
> >     ShowMessage((Graphic as TMyGraphic).FField);
> >   Graphic.Free;
> > end;

> --
> Martin Harvey.
> Totally rewritten web pages at:
> http://www.harvey27.demon.co.uk/mch24/

> "ALGOL 60 was a language so far ahead of its time that it
> was not only an improvement on its predecessors but also
> on nearly all its successors". C.A.R. Hoare

> --------------BEGIN GEEK CODE BLOCK--------------
> Version: 3.12
> GCS/CC d(+) s-:- a-- C+++$ UL@ P L@>++ E- W++
> N+++ o-- K++ w+++$ O--- M-- V-- PS@ Y-- PGP-
> t--- 5-- X-- R-- !tv b+ DI+ D+ G e++ h- r z++>---
> ---------------END GEEK CODE BLOCK---------------

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    

Re:Class Reference: what's wrong?


On Mon, 14 Dec 1998 01:55:12 +0300, "Andrew Anoshkin"

Quote
<v...@dataforce.net> wrote:
>Can anybody tell me why the constructor is not called in the following code.
>Very strange. The same technique works perfectly in the graphics.pas unit
>for registered TGraphic descendants.
>  GrClass := TMyGraphic;
>  Graphic := GrClass.Create;

The problem is that TGraphic.Create is protected, so your event cannot
call it. Instead, Delphi calls TObject.Create, which is public.

The virtual constructor feature of TGraphic classes is meant to be used
by TPicture when loading a graphics file.
--
Ray Lischner (http://www.tempest-sw.com/)
Author of "Hidden Paths of Delphi 3: Experts, Wizards, and the Open Tools API"

Re:Class Reference: what's wrong?


Quote
ullr...@math.okstate.edu wrote:

> > 3. Virtual methods only get linked in, if somewhere in the code there is
> > either an explicit construction call, or alternatively a call to
> > RegisterClasses....

>        Here you lose me. Is this the way it's supposed to work according
> to the docs?

No... not explicitly.

Quote
> I don't see anything about this under "constructors"; this
> sounds like you're saying a virtual constructor can't be invoked using
> a class-reference type unless you call RegisterClass first(???: is
> this a guess or something you know? How do you know? I'm all confused.)

Okay. The deal works like this: The linking rules for methods in delphi
are:

a) Non virtual methods get linked in if there is a compiled call to the
method.
b) Virtual methods get linked in if the compiler can tell that an
instance of that class type is created.

The particular problem that often occurs is when you convert a string to
a class type (using GetClass or FindClass), and then try to use that
class. At compile time, the compiler can't work out which classes will
be created at run time, and thus the optimising linker has a habit of
removing them. In addition, one needs to include the relevant RTTI, so
that the call to GetClass or FindClass will succeed.

However, the case you have is slightly less pathological than this. In
theory, the compiler should be able to tell that the
"MyClass:=TMyGraphic" statement means that TMyGraphic is being created
polymorphically... however, I guessed at the time that it might not have
figured this, and thus not worked out what to call, thus the guess that
RegisterClass might do the trick.

Failing that, one might like to check that all the typing info required
is there, by declaring the class inside a {$M+} block.

I've certainly had this problem before, but in my case, I was using
GetClass to set a class reference variable, and then calling the
constructor for that (checking the base class had a virtual constructor,
the class reference type was "class of" the class that had the virtual
constructor, and that all descendant constructors were overrides). I
then had no problems at all. The only difference between your code and
mine is that you *aren't* using GetClass.

Are you sure that the class reference variable is being given the right
value at run time? Have you checked the result of
MyClassRef.Create.ClassName ?

Quote
> > Have you tried putting RegisterClass(TMyGraphic) in the initialisation
> > section???

>        Have you tried it? I tried it just now, doesn't seem to make
> any difference.

Hmm.. yeah. In that case the problem proably isn't what I thought. It's
not very easy to tell without having a copy of the code to debug, not to
mention that fact that I really don't have the time to debug other
peoples code :-(

MH.

--
Martin Harvey.
Totally rewritten web pages at:
http://www.harvey27.demon.co.uk/mch24/

"ALGOL 60 was a language so far ahead of its time that it
was not only an improvement on its predecessors but also
on nearly all its successors". C.A.R. Hoare

--------------BEGIN GEEK CODE BLOCK--------------
Version: 3.12
GCS/CC d(+) s-:- a-- C+++$ UL@ P L@>++ E- W++
N+++ o-- K++ w+++$ O--- M-- V-- PS@ Y-- PGP-
t--- 5-- X-- R-- !tv b+ DI+ D+ G e++ h- r z++>---
---------------END GEEK CODE BLOCK---------------

Re:Class Reference: what's wrong?


In article <36780CB8.98EE8...@aziraphale.demon.co.uk>,
  Martin Harvey <mar...@aziraphale.demon.co.uk> wrote:

Quote
> ullr...@math.okstate.edu wrote:

> > > 3. Virtual methods only get linked in, if somewhere in the code there is
> > > either an explicit construction call, or alternatively a call to
> > > RegisterClasses....

> >        Here you lose me. Is this the way it's supposed to work according
> > to the docs?

> No... not explicitly.

> > I don't see anything about this under "constructors"; this
> > sounds like you're saying a virtual constructor can't be invoked using
> > a class-reference type unless you call RegisterClass first(???: is
> > this a guess or something you know? How do you know? I'm all confused.)

> Okay. The deal works like this: The linking rules for methods in delphi
> are:

> a) Non virtual methods get linked in if there is a compiled call to the
> method.
> b) Virtual methods get linked in if the compiler can tell that an
> instance of that class type is created.

        That's what I thought, hence my confusion.

- Show quoted text -

Quote
> The particular problem that often occurs is when you convert a string to
> a class type (using GetClass or FindClass), and then try to use that
> class. At compile time, the compiler can't work out which classes will
> be created at run time, and thus the optimising linker has a habit of
> removing them. In addition, one needs to include the relevant RTTI, so
> that the call to GetClass or FindClass will succeed.

> However, the case you have is slightly less pathological than this. In
> theory, the compiler should be able to tell that the
> "MyClass:=TMyGraphic" statement means that TMyGraphic is being created
> polymorphically... however, I guessed at the time that it might not have
> figured this, and thus not worked out what to call, thus the guess that
> RegisterClass might do the trick.

> Failing that, one might like to check that all the typing info required
> is there, by declaring the class inside a {$M+} block.

> I've certainly had this problem before, but in my case, I was using
> GetClass to set a class reference variable, and then calling the
> constructor for that (checking the base class had a virtual constructor,
> the class reference type was "class of" the class that had the virtual
> constructor, and that all descendant constructors were overrides). I
> then had no problems at all. The only difference between your code and
> mine is that you *aren't* using GetClass.

> Are you sure that the class reference variable is being given the right
> value at run time? Have you checked the result of
> MyClassRef.Create.ClassName ?

       That was the first thing I checked. Maybe the second, but yes,
I definitely checked that GrClassOrWhatever.ClassName was "TMyGraphic"
before deciding I was confused.

Quote
> > > Have you tried putting RegisterClass(TMyGraphic) in the initialisation
> > > section???

> >        Have you tried it? I tried it just now, doesn't seem to make
> > any difference.

> Hmm.. yeah. In that case the problem proably isn't what I thought. It's
> not very easy to tell without having a copy of the code to debug, not to
> mention that fact that I really don't have the time to debug other
> peoples code :-(

       Heh-heh. There is that, this one just bugged me because I
thought I knew how this aspect of it worked... luckily Lischner
has stepped in so now we know the real story.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    

Re:Class Reference: what's wrong?


In article <36780940.256414...@news.proaxis.com>,
  n...@junk.mail (Ray Lischner) wrote:

Quote
> On Mon, 14 Dec 1998 01:55:12 +0300, "Andrew Anoshkin"
> <v...@dataforce.net> wrote:

> >Can anybody tell me why the constructor is not called in the following code.
> >Very strange. The same technique works perfectly in the graphics.pas unit
> >for registered TGraphic descendants.

> >  GrClass := TMyGraphic;
> >  Graphic := GrClass.Create;

> The problem is that TGraphic.Create is protected, so your event cannot
> call it. Instead, Delphi calls TObject.Create, which is public.

       Aha - thanks.

       Actually I noticed that TGraphic.Create was protected,
decided that that couldn't be the problem because people mention
the same technique working in graphics.pas - went back to my own
code before checking this.

       This must be what the problem is. I don't quite get it, though,
must be missing something: since GrClass is an instance of TGraphicClass
the compiler _knows_ that the value of GrClass is going to be a descendant
of TGraphic. So why can't the call GrClass.Create get compiled to a call
to TGraphic.Create (which would be a VMT lookup resolving here to
TMyGraphic.Create at runtime)???

       Oh. Duh. Whether we're referring to an instance of a descendant
or not, there are no calls to TGraphic.Create except from code
_within_ a descendant of TGraphic (or inside graphics.pas), and that's
not where we are. Let's see, then all this would have worked if all
the code was in one unit...

       Darn, I thought I had it. But if I put the definition of
TMyGraphic in TempGraphics.pas and try to use that version it
still doesn't work. Just to be explicit: right now TempGraphics
looks just like Graphics except right around the word "implementation",
where it says

type
  TMyGraphic = class(TGraphic)
  public
    FField: string;
    constructor Create; override;
  end;

implementation

uses Consts;

constructor TMyGraphic.Create;
begin
  //Please call me!!!
  inherited Create;
  FField := 'something';
  MessageBeep(1);
end;

I make a new project using TempGraphics instead of Graphics, I say

uses TempGraphics;

procedure TForm1.Button1Click(Sender: TObject);
var
  GrClass: TGraphicClass;
  Graphic: TGraphic;
begin
  GrClass := TMyGraphic;
  Graphic := GrClass.Create;
  if Graphic is TMyGraphic then
    ShowMessage((Graphic as TMyGraphic).FField);
  Graphic.Free;
end;

and it still doesn't work right. Would you have thought this
would work or I'm still missing something or what?

Quote
> The virtual constructor feature of TGraphic classes is meant to be used
> by TPicture when loading a graphics file.
> --
> Ray Lischner (http://www.tempest-sw.com/)
> Author of "Hidden Paths of Delphi 3: Experts, Wizards, and the Open Tools API"

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    

Re:Class Reference: what's wrong?


Quote
On Thu, 17 Dec 1998 20:40:07 GMT, ullr...@math.okstate.edu wrote:
>       Actually I noticed that TGraphic.Create was protected,
>decided that that couldn't be the problem because people mention
>the same technique working in graphics.pas - went back to my own
>code before checking this.

It works within Graphics.pas because in the same unit, everything is
public. Thus, TPicture can call protected, and even private methods of
TGraphic because both classes are declared in the same unit.

The solution is not to try to call TGraphic.Create, but to register your
TGraphic-derived class with TPicture and use TPicture.
--
Ray Lischner (http://www.tempest-sw.com/)
Author of "Hidden Paths of Delphi 3: Experts, Wizards, and the Open Tools API"

Re:Class Reference: what's wrong?


Quote
ullr...@math.okstate.edu wrote:

>        Actually I noticed that TGraphic.Create was protected,
> decided that that couldn't be the problem because people mention
> the same technique working in graphics.pas - went back to my own
> code before checking this.

Yeah. But things declared in the same unit can access it. Things
declared in other units Can't. An old Delphi Gotcha :-)

MH.

--
Martin Harvey.
Totally rewritten web pages at:
http://www.harvey27.demon.co.uk/mch24/

"ALGOL 60 was a language so far ahead of its time that it
was not only an improvement on its predecessors but also
on nearly all its successors". C.A.R. Hoare

--------------BEGIN GEEK CODE BLOCK--------------
Version: 3.12
GCS/CC d(+) s-:- a-- C+++$ UL@ P L@>++ E- W++
N+++ o-- K++ w+++$ O--- M-- V-- PS@ Y-- PGP-
t--- 5-- X-- R-- !tv b+ DI+ D+ G e++ h- r z++>---
---------------END GEEK CODE BLOCK---------------

Other Threads