Wrong constructor returning the right object

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Wrong constructor returning the right object

Mark Morgan Lloyd-5
I think I've sorted this but I'd appreciate being kicked if it looks
like I'm doing something badly wrong.

What determines the constructor that's called when an object's created?

I've got a couple of classes defined like this:

   TB5500BaseUnit=
     class(TObject)
       constructor Create(enum: integer; mnem: String7; unitIndex:
integer; unitDesignate: integer; cc: TObject);
..

   TB5500SPOUnit=
     class (TB5500BaseUnit)
       constructor Create(enum: integer; mnem: String7; unitIndex:
integer; unitDesignate: integer; cc: TObject);
..

The actual objects being created are in response to configuration tables
and files:

var
   unitClass: class of TB5500BaseUnit; (* Ancestor of all I/O devices *)
   u: TB5500BaseUnit;
   name: string;                       (* For debugging *)
..

         unitClass := LookupUnitType( { from device name } );
         name := unitClass.ClassName;    (* For debugging *)
         name := '';                     (* Make sure it's wiped *)
         u := unitClass.Create(Ord(mnem), specs.unitName,
specs.unitIndex, specs.designate, self);
         name := u.ClassName;            (* For debugging *)
         thisUnit[specs.unitIndex] := u;

If I was trying to create a TB5500SPOUnit I was finding that unitclass
was set correctly, and that u was getting a valid type with (later)
public methods declared as overrides being available. The odd thing was
that TB5500SPOUnit.Create() was not being called, but instead
TB5500BaseUnit.Create().

I find that if I explicitly decorate TB5500BaseUnit.Create() as virtual
and TB5500SPOUnit.Create() as override then TB5500SPOUnit.Create() is
called correctly. I didn't think this was necessary, but on reflection I
assume that it's because u is declared as a TB5500BaseUnit: the
variable's compile-time rather than run-time class is being used for the
constructor unless explicitly overridden.

Is there an obvious way of tiying this up that I'm overlooking?

--
Mark Morgan Lloyd
markMLl .AT. telemetry.co .DOT. uk

[Opinions above are the author's, not those of his employers or colleagues]
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Wrong constructor returning the right object

Jeppe Johansen-3
Den 23-02-2014 15:26, Mark Morgan Lloyd skrev:

> I think I've sorted this but I'd appreciate being kicked if it looks
> like I'm doing something badly wrong.
>
> What determines the constructor that's called when an object's created?
>
> I've got a couple of classes defined like this:
>
>   TB5500BaseUnit=
>     class(TObject)
>       constructor Create(enum: integer; mnem: String7; unitIndex:
> integer; unitDesignate: integer; cc: TObject);
> ..
>
>   TB5500SPOUnit=
>     class (TB5500BaseUnit)
>       constructor Create(enum: integer; mnem: String7; unitIndex:
> integer; unitDesignate: integer; cc: TObject);
> ..
>
> The actual objects being created are in response to configuration
> tables and files:
>
> var
>   unitClass: class of TB5500BaseUnit; (* Ancestor of all I/O devices *)
>   u: TB5500BaseUnit;
>   name: string;                       (* For debugging *)
> ..
>
>         unitClass := LookupUnitType( { from device name } );
>         name := unitClass.ClassName;    (* For debugging *)
>         name := '';                     (* Make sure it's wiped *)
>         u := unitClass.Create(Ord(mnem), specs.unitName,
> specs.unitIndex, specs.designate, self);
>         name := u.ClassName;            (* For debugging *)
>         thisUnit[specs.unitIndex] := u;
>
> If I was trying to create a TB5500SPOUnit I was finding that unitclass
> was set correctly, and that u was getting a valid type with (later)
> public methods declared as overrides being available. The odd thing
> was that TB5500SPOUnit.Create() was not being called, but instead
> TB5500BaseUnit.Create().
>
> I find that if I explicitly decorate TB5500BaseUnit.Create() as
> virtual and TB5500SPOUnit.Create() as override then
> TB5500SPOUnit.Create() is called correctly. I didn't think this was
> necessary, but on reflection I assume that it's because u is declared
> as a TB5500BaseUnit: the variable's compile-time rather than run-time
> class is being used for the constructor unless explicitly overridden.
>
> Is there an obvious way of tiying this up that I'm overlooking?
>
You have forgotten to declare the constructor virtual.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Wrong constructor returning the right object

Mark Morgan Lloyd-5
Jeppe Græsdal Johansen wrote:

>> I find that if I explicitly decorate TB5500BaseUnit.Create() as
>> virtual and TB5500SPOUnit.Create() as override then
>> TB5500SPOUnit.Create() is called correctly. I didn't think this was
>> necessary, but on reflection I assume that it's because u is declared
>> as a TB5500BaseUnit: the variable's compile-time rather than run-time
>> class is being used for the constructor unless explicitly overridden.
>>
>> Is there an obvious way of tiying this up that I'm overlooking?
>>
> You have forgotten to declare the constructor virtual.

I said I'd fixed it by declaring the constructor virtual, but that I'm
not entirely confident in my understanding of why that's necessary.

--
Mark Morgan Lloyd
markMLl .AT. telemetry.co .DOT. uk

[Opinions above are the author's, not those of his employers or colleagues]
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Wrong constructor returning the right object

Marco van de Voort
In reply to this post by Mark Morgan Lloyd-5
In our previous episode, Mark Morgan Lloyd said:

That logical, since the constructors are not an virtual; and override; pair.
 
> I find that if I explicitly decorate TB5500BaseUnit.Create() as virtual
> and TB5500SPOUnit.Create() as override then TB5500SPOUnit.Create() is
> called correctly.

That's the normal way.

> I didn't think this was necessary,

_why_ did you think this?

> but on reflection I
> assume that it's because u is declared as a TB5500BaseUnit: the
> variable's compile-time rather than run-time class is being used for the
> constructor unless explicitly overridden.

In theory this kind of information could propagate in simple cases, but then
behaviour would differ between cases where this information could be
propagated (a deeper version called), and not (base version called).

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Wrong constructor returning the right object

Mark Morgan Lloyd-5
Marco van de Voort wrote:

> In our previous episode, Mark Morgan Lloyd said:
>
> That logical, since the constructors are not an virtual; and override; pair.
>  
>> I find that if I explicitly decorate TB5500BaseUnit.Create() as virtual
>> and TB5500SPOUnit.Create() as override then TB5500SPOUnit.Create() is
>> called correctly.
>
> That's the normal way.
>
>> I didn't think this was necessary,
>
> _why_ did you think this?

In part, because the documentation on method declarations at
http://www.freepascal.org/docs-html/ref/refsu25.html#x66-730005.5.1 
explicitly allows a  virtual  method directive, but the documentation on
constructor declarations at
http://www.freepascal.org/docs-html/ref/refse28.html#x64-710005.4 
explicitly does not allow it.

>> but on reflection I
>> assume that it's because u is declared as a TB5500BaseUnit: the
>> variable's compile-time rather than run-time class is being used for the
>> constructor unless explicitly overridden.
>
> In theory this kind of information could propagate in simple cases, but then
> behaviour would differ between cases where this information could be
> propagated (a deeper version called), and not (base version called).

--
Mark Morgan Lloyd
markMLl .AT. telemetry.co .DOT. uk

[Opinions above are the author's, not those of his employers or colleagues]
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Wrong constructor returning the right object

Marco van de Voort
In our previous episode, Mark Morgan Lloyd said:
> >> I didn't think this was necessary,
> >
> > _why_ did you think this?
>
> In part, because the documentation on method declarations at
> http://www.freepascal.org/docs-html/ref/refsu25.html#x66-730005.5.1 
> explicitly allows a  virtual  method directive, but the documentation on
> constructor declarations at

That link also contains a case for constructors, but point taken, the
railroad diagram of constructor could be improved, which might be worthy of
a bugreport.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Wrong constructor returning the right object

Mark Morgan Lloyd-5
Marco van de Voort wrote:

> In our previous episode, Mark Morgan Lloyd said:
>>>> I didn't think this was necessary,
>>> _why_ did you think this?
>> In part, because the documentation on method declarations at
>> http://www.freepascal.org/docs-html/ref/refsu25.html#x66-730005.5.1 
>> explicitly allows a  virtual  method directive, but the documentation on
>> constructor declarations at
>
> That link also contains a case for constructors, but point taken, the
> railroad diagram of constructor could be improved, which might be worthy of
> a bugreport.

"Railroad diagram"- I've not heard it called that since I worked for the
Yanks :-) Bugreported as http://mantis.freepascal.org/view.php?id=25770

I also note the comment about classes with virtual methods /having/ to
have an explicit constructor and destructor: does this apply to classes
that have *override* methods or only to parent classes with one or more
*virtual* methods?

--
Mark Morgan Lloyd
markMLl .AT. telemetry.co .DOT. uk

[Opinions above are the author's, not those of his employers or colleagues]
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Wrong constructor returning the right object

Jeppe Johansen-3
In reply to this post by Mark Morgan Lloyd-5
Den 23-02-2014 17:03, Mark Morgan Lloyd skrev:

> Marco van de Voort wrote:
>> In our previous episode, Mark Morgan Lloyd said:
>>
>> That logical, since the constructors are not an virtual; and
>> override; pair.
>>
>>> I find that if I explicitly decorate TB5500BaseUnit.Create() as
>>> virtual and TB5500SPOUnit.Create() as override then
>>> TB5500SPOUnit.Create() is called correctly.
>>
>> That's the normal way.
>>
>>> I didn't think this was necessary,
>>
>> _why_ did you think this?
>
> In part, because the documentation on method declarations at
> http://www.freepascal.org/docs-html/ref/refsu25.html#x66-730005.5.1 
> explicitly allows a  virtual  method directive, but the documentation
> on constructor declarations at
> http://www.freepascal.org/docs-html/ref/refse28.html#x64-710005.4 
> explicitly does not allow it.
That documentation is about objects, not classes.

Here's the relevant part for classes:
http://www.freepascal.org/docs-html/ref/refsu25.html#x66-730005.5.1

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal