Topic: proposal: virtual calls in constructor


Author: petebecker@acm.org (Pete Becker)
Date: Mon, 6 Jan 2003 03:43:06 +0000 (UTC)
Raw View
Matt Austern wrote:
>
> You're talking about code size.  I'm talking about data size.  Under
> certain circumstances (which aren't the most common case, but also
> aren't ridiculously rare), you need to have vtables that are never
> used except at intermediate stages in constructors and destructors.

Yup. Which is why I asked what alternatives you preferred, because the
two that I can see have more serious problems.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: brangdon@cix.co.uk (Dave Harris)
Date: Mon, 6 Jan 2003 03:48:46 +0000 (UTC)
Raw View
gdr@integrable-solutions.net (Gabriel Dos Reis) wrote (abridged):
> How smart should a C++ compiler be?

Or rather, how smart should the standard require them to be. The trouble
with mandating optimisations is that you need a clear vision of the
implementation. If you get it wrong, they can become pessimisations.

In practice implementors like (a) a clear and precise definition of what
behaviour is required; (b) complete freedom in how they achieve it. That
allows them to produce the best implementations in the long run. There is
a separation of concerns here. It's not the standard's job to anticipate
what compiler technologies might develop over the next decade.


> How many smart C++ compilers are out of there for daily use that do
> the optimization you're suggesting?

I know of none. I expect that's a reflection of the priorities of vendors
and their market. They have limited resources. Many of them don't even
have 100% conforming compilers yet. Surely they should make it right
before they optimise?

Actually Microsoft has something like __novtable, which is a related
low-level hack. Here's the thing: even if there are no virtual function
calls in the base constructor, you can only avoid setting up the vtable
there if you know there is a derived constructor which /will/ set it up.
In other words, you have to know the base class will never be instantiated
directly. You can tell that from the class declaration if it has a pure
virtual function, otherwise it needs a whole-program analysis. The
Microsoft extension short-cuts that analysis, with the obvious sad
consequences if the programmer uses it mistakenly.

I suspect that changing the standard to forbid calling virtual functions
in constructors would not help very much, because many classes would
still need that whole-program analysis before they could omit their
vtable.

  Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
      brangdon@cix.co.uk      |   And close your eyes with holy dread,
                              |  For he on honey dew hath fed
 http://www.bhresearch.co.uk/ |   And drunk the milk of Paradise."

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: petebecker@acm.org (Pete Becker)
Date: Sat, 4 Jan 2003 19:27:17 +0000 (UTC)
Raw View
Matt Austern wrote:
>
> petebecker@acm.org (Pete Becker) writes:
>
> > csklu wrote:
> > >
> > > I think it has been well established that it is not legal (or at least
> > > not advisable) to call virtual functions in constructors,
> >
> > In general it's well defined, meaningful, and occasionally useful.
> > What's not allowed is making a virtual call to a pure virtual function.
>
> Occasionally useful, a serious pain to implement,

Simple to implement: plug in the vtable pointer at the start of each
ctor and at the end of each dtor.

> and a feature that
> has cost (mostly space, not time) even for programs that don't use it.

Yup: one instruction per ctor and dtor.

> This is number one on my list of C++ features that I wish had never
> gotten into the standard.
>

What alternative do you prefer? I see two, both of which are far worse.
First, Java-like behavior of calling virtual functions on the most
derived (i.e. unconstructed) object (less of a problem in Java, which
guarantees default initialization, even before constructors run, but
still a pain if you have to write robust virtual functions). Second,
make the behavior of a call to a virtual function from a ctor or dtor
undefined (in which case the current behavior is, of course, permitted).

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: austern@well.com (Matt Austern)
Date: Sat, 4 Jan 2003 20:05:21 +0000 (UTC)
Raw View
petebecker@acm.org (Pete Becker) writes:

> Matt Austern wrote:
> >
> > petebecker@acm.org (Pete Becker) writes:
> >
> > > csklu wrote:
> > > >
> > > > I think it has been well established that it is not legal (or at least
> > > > not advisable) to call virtual functions in constructors,
> > >
> > > In general it's well defined, meaningful, and occasionally useful.
> > > What's not allowed is making a virtual call to a pure virtual function.
> >
> > Occasionally useful, a serious pain to implement,
>
> Simple to implement: plug in the vtable pointer at the start of each
> ctor and at the end of each dtor.
>
> > and a feature that
> > has cost (mostly space, not time) even for programs that don't use it.
>
> Yup: one instruction per ctor and dtor.

You're talking about code size.  I'm talking about data size.  Under
certain circumstances (which aren't the most common case, but also
aren't ridiculously rare), you need to have vtables that are never
used except at intermediate stages in constructors and destructors.
I could go into a bit more detail, but that sort of in-depth
discussion of implementation techniques would probably be more
appropriate in some other newsgroups.

Now admittedly, we're talking about per-class space overhead rather
than per-object.  Still, it's annoying.  It can be tens of kilobytes
of overhead for a feature that most users don't care about.  I wish I
didn't have to do it.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: brangdon@cix.co.uk (Dave Harris)
Date: Sun, 5 Jan 2003 03:01:02 +0000 (UTC)
Raw View
austern@well.com (Matt Austern) wrote (abridged):
> You're talking about code size.  I'm talking about data size.  Under
> certain circumstances (which aren't the most common case, but also
> aren't ridiculously rare), you need to have vtables that are never
> used except at intermediate stages in constructors and destructors.
> I could go into a bit more detail, but that sort of in-depth
> discussion of implementation techniques would probably be more
> appropriate in some other newsgroups.
>
> Now admittedly, we're talking about per-class space overhead rather
> than per-object.  Still, it's annoying.  It can be tens of kilobytes
> of overhead for a feature that most users don't care about.  I wish I
> didn't have to do it.

If they don't care about it - if they don't call any virtual functions in
their constructor or destructor - a smart implementation will often be
able to optimise it away. Especially as the entire source of the class is
likely to be within a single compilation unit.

  Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
      brangdon@cix.co.uk      |   And close your eyes with holy dread,
                              |  For he on honey dew hath fed
 http://www.bhresearch.co.uk/ |   And drunk the milk of Paradise."

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: gdr@integrable-solutions.net (Gabriel Dos Reis)
Date: Sun, 5 Jan 2003 04:52:51 +0000 (UTC)
Raw View
brangdon@cix.co.uk (Dave Harris) writes:

| austern@well.com (Matt Austern) wrote (abridged):
| > You're talking about code size.  I'm talking about data size.  Under
| > certain circumstances (which aren't the most common case, but also
| > aren't ridiculously rare), you need to have vtables that are never
| > used except at intermediate stages in constructors and destructors.
| > I could go into a bit more detail, but that sort of in-depth
| > discussion of implementation techniques would probably be more
| > appropriate in some other newsgroups.
| >
| > Now admittedly, we're talking about per-class space overhead rather
| > than per-object.  Still, it's annoying.  It can be tens of kilobytes
| > of overhead for a feature that most users don't care about.  I wish I
| > didn't have to do it.
|
| If they don't care about it - if they don't call any virtual functions in
| their constructor or destructor - a smart implementation will often be
| able to optimise it away.

I believe one of the key word is there: smart.  How smart should a C++
compiler be?  How many smart C++ compilers are out of there for
daily use that do the optimization you're suggesting?

Should the C++ definition be set so as to require an Asgard technology?

--
Gabriel Dos Reis, gdr@integrable-solutions.net

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: csklu_news@yahoo.com (csklu)
Date: Fri, 3 Jan 2003 01:39:58 +0000 (UTC)
Raw View
> <csklu_news@yahoo.com> writes
> > This
> >would still have the restriction that virtual methods can't be called
> >in initializer lists, but I think that would be more acceptable.
>
> That kills it stone dead for me. There should be no difference between a
> function call in an initialiser list and the same call in the body of a
> ctor.

Any other possible solutions then?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: philippe_mori@hotmail.com ("Philippe Mori")
Date: Fri, 3 Jan 2003 05:19:37 +0000 (UTC)
Raw View
> I think it has been well established that it is not legal (or at least
> not advisable) to call virtual functions in constructors, but why
> shouldn't it be made possible (by the standard)?
>
> [...]
>
> My suggestion would be to make construction of objects occur in two
> phases. The first pass would be to initialize the initializer lists
> (starting from the base to the derived) including the initialization
> of the virtual table, and then the second pass would be the actual
> body of the constructor(s). In this manner, the object would be
> completely initialized even in the base class part's constructor. This
> would still have the restriction that virtual methods can't be called
> in initializer lists, but I think that would be more acceptable. Any
> thoughts on this? Has this already been addressed? (I've heard that
> some compilers allow for this, but then this makes non-portable code).
>
> Regards,
> Chris
>

C++ Builder does support virtual calls in constructor for VCL classes. For
those classes, the object is first initilized to 0 (ignoring the
initialization list), the virtual table is then setup and then the
constructor are called the standard way but with the complete object v-table
instead of the current level one as it is normally the case. Thus calling a
virtual function from a constructor do make a virtual call. This was done
that way for compatibility with Delphi on which VCL is based.

I do like the idea but I do think it should be implemented another way:

1) We should allows to specify 0-init on a class to initialize all member to
0 from that level down to the concrete type prior to running the
constructor. The compiler would be allowed to optimize that away if
something will be initialized afterward and it can be prooved that the
0-initialisation is useless (even if an exception occurs). This could even
be done in part and can be constructor-dependant.

2) Constructors would be called the standard way as it is the case now.

3) Either another function would be called for virtual initialisation (say
virtual_init()) or the syntax of the constructor would be extended to allows
for a virtual part or to specify a function to call virtually when the
object would be fully constructed. So the possibility are:

    a) A fixed name function that is called virtually if it exists. That
function should ideally automatically call any such function in all of its
base classes.

    b) The constructor would have 2 blocks. One for the first (non virtual)
pass and one for the second. The syntax could be something like C::C(int i)
: a_member(i) { /* pass 1 */ } virtual { /* pass 2 */ }. probably virtual
would need to be replaced by something else to avoid parsing ambiguities...

    c) It would be possible to specify a function to call (virtually) once
the object is fully constructed. The constructor syntax would allows to
specify the function to call with any optional arguments. It could be
something like C::C(int i) : a_member(i), virtual_init(f(i + 3)) { } where
virtual_init would be a reserved keyword, f would be the function to call
(arguments could be specified). If the function is virtual, the call in the
second pass would be virtual, if not, it would be static. With that
solution, no automatic calling of base class member would occurs... If this
is required, then the function must not be virtual (in that case the
compiler would call any even if the same function exists in a derived class
(it would even be possible to uses a function with the same name in derived
class)). It would propably be an error to call the same "physical" function
from different level constructor (That is if a derived constructor specify a
function that only exist in a base class and that base class (or another
class between them) already uses that same function.)

4) Finally, there would also be a symetrical cleanup functionality for
destructors except that no argument could be provided.

5) If a constructor throws an exception during the second pass, then cleanup
function would be called for level where the second pass was completed.
Afterward, standard destructor would be called for the whole object.

I do think that it would be a great addition since virtual initialisation
(or destruction) cannot be handled transparently. I know that we can have
the same effect by calling a virtual function after the object is created
(or before the object is destroyed) but since it is not supported by the
standard, we cannot easily enforce it (except by using a factory for
creation or using some proxies if we want to support both creation and
destruction).

Options that allows to specify parameters are more interesting but may be a
bit harder to implement. Also solutions with distinct functions would enable
to allows inlining or not for both part instead of having the same thing in
both cases.


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: austern@well.com (Matt Austern)
Date: Fri, 3 Jan 2003 05:19:58 +0000 (UTC)
Raw View
petebecker@acm.org (Pete Becker) writes:

> csklu wrote:
> >
> > I think it has been well established that it is not legal (or at least
> > not advisable) to call virtual functions in constructors,
>
> In general it's well defined, meaningful, and occasionally useful.
> What's not allowed is making a virtual call to a pure virtual function.

Occasionally useful, a serious pain to implement, and a feature that
has cost (mostly space, not time) even for programs that don't use it.
This is number one on my list of C++ features that I wish had never
gotten into the standard.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: terekhov@web.de (Alexander Terekhov)
Date: Sat, 4 Jan 2003 00:25:06 +0000 (UTC)
Raw View
csklu wrote:
[...]
> Any other possible solutions then?

Sure. Deprecate the current "semi-polymorphic-behavior" for objects
under construction/destruction and introduce an optional mechanism
of post-ctors and pre-dtors ("poly"/"nonpoly" ptr|ref type qualifier
aside for a moment ;-) ). < refs/info >

http://tinyurl.com/4190
(Subject: Re: Constructor & virtual methods question)

http://tinyurl.com/4192
(Subject: Re: Problem: Pure virtual functions in constructors /
 statics can't be virtual.)

http://tinyurl.com/4194
(Subject: Re: Constructor Failures)

http://tinyurl.com/4197
(Subject: Re: volatile, was: memory visibility between threads)

regards,
alexander.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis.glassborow@ntlworld.com (Francis Glassborow)
Date: Sat, 4 Jan 2003 00:25:28 +0000 (UTC)
Raw View
In article <f9538a81.0301021735.1483808d@posting.google.com>, csklu
<csklu_news@yahoo.com> writes
>> <csklu_news@yahoo.com> writes
>> > This
>> >would still have the restriction that virtual methods can't be called
>> >in initializer lists, but I think that would be more acceptable.
>>
>> That kills it stone dead for me. There should be no difference between a
>> function call in an initialiser list and the same call in the body of a
>> ctor.
>
>Any other possible solutions then?

I can think of none that does not break vast amounts of existing code. I
think that the only fix to this 'problem' is better education.

--
ACCU Spring Conference 2003 April 2-5
The Conference you cannot afford to miss
Check the details: http://www.accuconference.co.uk/
Francis Glassborow      ACCU

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: csklu_news@yahoo.com (csklu)
Date: Thu, 2 Jan 2003 19:11:23 +0000 (UTC)
Raw View
I think it has been well established that it is not legal (or at least
not advisable) to call virtual functions in constructors, but why
shouldn't it be made possible (by the standard)? The usual reasoning
for disallowing this is (from what I understand) as follows:

In most implementations, virtual methods are maintained in a vtable.
In base classes, this vtable is initialised to point to the methods in
the base class and then the derived class part overwrites these
entries in its own constructor. In the base constructor, the derived
part hasn't had a chance to overwrite these entries yet so the base
class' methods get called.

My suggestion would be to make construction of objects occur in two
phases. The first pass would be to initialize the initializer lists
(starting from the base to the derived) including the initialization
of the virtual table, and then the second pass would be the actual
body of the constructor(s). In this manner, the object would be
completely initialized even in the base class part's constructor. This
would still have the restriction that virtual methods can't be called
in initializer lists, but I think that would be more acceptable. Any
thoughts on this? Has this already been addressed? (I've heard that
some compilers allow for this, but then this makes non-portable code).

Regards,
Chris

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: francis.glassborow@ntlworld.com (Francis Glassborow)
Date: Thu, 2 Jan 2003 19:50:44 +0000 (UTC)
Raw View
In article <f9538a81.0301021105.28f4e2e8@posting.google.com>, csklu
<csklu_news@yahoo.com> writes
> This
>would still have the restriction that virtual methods can't be called
>in initializer lists, but I think that would be more acceptable.

That kills it stone dead for me. There should be no difference between a
function call in an initialiser list and the same call in the body of a
ctor.

--
ACCU Spring Conference 2003 April 2-5
The Conference you cannot afford to miss
Check the details: http://www.accuconference.co.uk/
Francis Glassborow      ACCU

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: petebecker@acm.org (Pete Becker)
Date: Thu, 2 Jan 2003 20:25:18 +0000 (UTC)
Raw View
csklu wrote:
>
> I think it has been well established that it is not legal (or at least
> not advisable) to call virtual functions in constructors,

In general it's well defined, meaningful, and occasionally useful.
What's not allowed is making a virtual call to a pure virtual function.

> but why
> shouldn't it be made possible (by the standard)? The usual reasoning
> for disallowing this is (from what I understand) as follows:
>
> In most implementations, virtual methods are maintained in a vtable.
> In base classes, this vtable is initialised to point to the methods in
> the base class and then the derived class part overwrites these
> entries in its own constructor. In the base constructor, the derived
> part hasn't had a chance to overwrite these entries yet so the base
> class' methods get called.

That's the usual mechanism for implementing what the standard requires.
It is not the reason for the requirement.

The reason for the requirement is that when the base class's constructor
is running the derived class's constructor has not run. The author of
the base class has no way of knowing which virtual functions, overridden
in the derived class, will work correctly with uninitialized data and
which will not. So the rule is that you get the version defined for the
class whose constructor is currently running. The alternative would be
to require class implementors to document every virtual function that's
called from all constructors and the destructor so that derived class
implementors can make sure that overriders of those functions do not
depend on their class being initialized. Such functions can't do much of
anything, so there's little benefit from the added coupling.

--

Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]