Topic: post-ctors [ was As long as everyone is asking for new features]


Author: Christopher Eltschka <celtschk@web.de>
Date: Thu, 21 Jun 2001 18:04:24 GMT
Raw View
Michiel Salters<Michiel.Salters@cmg.nl> writes:

> In article <3B27ABC6.B7E670F8@bear.com>, Matvei Brodski says...
> >
> >Michiel Salters wrote:

[...]

> >You can not make the same kind of statement about post-constructed
> >moment. Nothing substantial happens at the time. No compiler help
> >is needed to find out if we reached it.
>
> Why not? The post-condition of the post-constructor would be that we can
> call all "normal" functions safely after that time, which we couldn't do
> before. IF the post-constructor doesn't ADD this guarantee it isn't needed.
> So a post-constructor would ALWAYS be associated with a logical state
> transition, a capability change. And just as post-constructors are designed
> to run after the first state transition, post-post-constructors are
> designed to run after this second transition.

The constructors do the state transition of the _object_ from
"not existant" to "fully constructed".

The post-constructors do the state transition of the _environment_
from "does not know about the object" to "does know about the object".

Indeed, I don't think a proper written post-constructor should change
the object's logical state in any way.

I do consider the "base + template final" pattern as sufficient
implementation of post-constructors (modulo constructor argument
forwarding problems, which hopefully will be solved in C++0x);
however, this doesn't change any arguments about them making sense; it
only means that language support for them is IMHO sufficient.

>
> As a result we'll need to have something like this:
>
> class C : public ... {
> virtual f() { } // can be called after ctor
> virtual[0] g() { } // same as unqualified virtual
> virtual[1] h() { } // can be called after post-ctor, 2nd vtable setup
> virtual[2] j() { } // 3rd vtable setup. Can be called after post-post-ctor.
> }
> to be entirely consistent.

No, the usability of the object isn't affected by the
post-constructor.  Only the environment is affected. At least for what
I consider a correctly implemented post constructor.

IMHO if there's a function which cannot be called before execution of
the post-constructor, it's no better than a function which cannot be
called before call to an init function.

>
> This feature is troubled by dependancy inversions: if the completion of
> the Base object depends on the Derived object being complete and vice versa,
> we cannot guarantee that either object is ever completed. So C++ has the
> hard rule that the Base object must be completed before the Derived object
> is being completed.

And post-constructors don't change this rule.  After the constructor
executed, the object is fully constructed and should be completely
usable.

[...]

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Michael Lee Finney <michael.finney@acm.org>
Date: Mon, 18 Jun 2001 18:14:30 GMT
Raw View
In article <3B2A8167.FDCDA4A4@bear.com>,
mbrodski@bear.com says...
> "Post-constructor" from the scratch:

First, let me say that I am generally in favor of post
constructors and that I agree with your description.
This is because I have occasionally found myself in
exactly the same situation where either I had to force
the client to call a final initialization function, or
I had to do something really clunky.

Along those lines, the best that I have is to define,
for each class in the hierarchy, a static method
create() which is overloaded in parallel with the
constructor overloads. Make the constructors protected
and the create() methods public. Then each create()
method calls the appropriate constructor, and
afterwards calls the initialization function.

Virtual functions aren't needed because the static
type is known at construction time.

Then, instead of saying:

   new MyClass(...)

the client says:

   MyClass::create(...)

and all the post construction nastiness is hidden
away. The difference in syntax isn't too bad, so there
isn't any real impact on the client.

More concretely...

   class MyClass
      {
      protected:
         MyClass();
      public:
         static MyClass * create();

         void init();
      };

   MyClass * MyClass::create()
      {
      MyClass * myClass = new MyClass();
      myClass->init();
      return myClass;
      }

This is a bit more trouble than just defining init()
with a bit of syntactic sugar, but not much and it
does the job just as effectively.

Since the post constructor, in either case, is only
called by the new operator or by create(), the static
type of the object is known at that time and init()
does not need to be virtual. It may itself, however,
need to call virtual functions, but that is ok because
the vtbl is completely constructed in either case
whenever init() is called.

Indeed, there are other advantages in that you can use
more than one name for the constructor which is,
unfortunately, not possible with C++. I find that to
be desirable also and the choice can be to either do
something like the above or to add useless arguments
to disambiguate the constructor.

In a recent example, I have a constructor which
requires some expensive validation of the parameters,
but which must be available for the client. However,
there are quite a few places where "trusted" code uses
the constructor and validation is not necessary. Since
the parameter list is identical, I have to add an
unused type to the end of the trusted constructor and
hope the compiler optimizes it away (almost certainly
it won't optimize EVERYTHING away, but it is better
than doing the expensive validation check).

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Michiel Salters<Michiel.Salters@cmg.nl>
Date: Fri, 15 Jun 2001 14:25:00 CST
Raw View
In article <3B27ABC6.B7E670F8@bear.com>, Matvei Brodski says...
>
>Michiel Salters wrote:
>
>> In article <usnh51jfs.fsf@worldnet.att.net>, Thomas A. Horsley says...
>>
>> >Maybe I should cast this in a completely different light: What I think is
>> >that the compiler (and absolutely nobody else right now) is the only one
>> >that knows when an entire object has been completely constructed.  If I want
>> >to run some code once the object is constructed (and I encounter this
>> >situation with extreme frequency), then the only completely reliable way to
>> >make sure that code is always run, is to have a compiler extension to
>> >support some new way of running code after the object is constructed.
>> >Hence the name "post constructor".
>>
>> That's not true, see my posts earlier about using a base class that forces
>> the most-derived class to be a template, provided by the base class author.
>
>Not good enough. The idea is that we want to use a "template method"
>pattern in initialization. This way a designer of base class can
>make sure that certain steps are made during initialization while
>leaving some details to Derived classes for specialization.
>And, yes, one can always write something like:

>Derived d;
>d.post_initialize();
>
>But this is not a solution. Just as
>
>Derived d;
>d.initialize();
>
>is a poor substitute for having a constructor.

That isn't what I proposed. The method I proposed makes
Derived d;               // Is an abstract class. [1]
illegal. Instead one would have to write
Connect<Derived> d;
where Connect<T> is provided by the base class author.
Connect<Derived>::Connect runs after Derived::Derived has
initialized its vtable. And yes, the Derived author can
specialize Connect<Derived>.

>> >This is also why I don't know what "post post" constructor means.
>> >
>> >There is only one point in time when the object has been fully constructed.
>>
>> When the constructor of the most derived class has run. And there would be
>> only one point in time when the object has been fully post-constructed.
>
>Then you can easily point out the crucial difference between the
>state before and after that point of "object is post-constructed".
>Say, when we talk about the point where the object is fully constructed
>we can say this:
>
>1.BEFORE object is "fully constructed": vtable (or whatever is used to
>provide for polymorphism) is not finalized, so we can not call a
>virtual function and expect it to be specialized for the type of
>the real object being constructed.
>AFTER object is "fully constructed": one can call virtual functions
>knowing that the most specialized version will be called.
>
>2.Without a compiler help we can not determine from inside the constructor
>body if we reached this point.

Not from the base ctor, no. But the author of the base class can write
another (friend) class for which that guarantee is there.

>You can not make the same kind of statement about post-constructed
>moment. Nothing substantial happens at the time. No compiler help
>is needed to find out if we reached it.

Why not? The post-condition of the post-constructor would be that we can
call all "normal" functions safely after that time, which we couldn't do
before. IF the post-constructor doesn't ADD this guarantee it isn't needed.
So a post-constructor would ALWAYS be associated with a logical state
transition, a capability change. And just as post-constructors are designed
to run after the first state transition, post-post-constructors are
designed to run after this second transition.

As a result we'll need to have something like this:

class C : public ... {
virtual f() { } // can be called after ctor
virtual[0] g() { } // same as unqualified virtual
virtual[1] h() { } // can be called after post-ctor, 2nd vtable setup
virtual[2] j() { } // 3rd vtable setup. Can be called after post-post-ctor.
}
to be entirely consistent.

This feature is troubled by dependancy inversions: if the completion of
the Base object depends on the Derived object being complete and vice versa,
we cannot guarantee that either object is ever completed. So C++ has the
hard rule that the Base object must be completed before the Derived object
is being completed.

If your Base objects can't, break them in two parts. One part is created
before Derived, one after. That is *exactly* what my solution is all
about.

Regards,
Michiel Salters

[1] Credits for this methods go to Christopher Eltschka. Thanks!

--
Michiel Salters
Consultant Technical Software Engineering
CMG Trade, Transport & Industry
Michiel.Salters@cmg.nl

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: news_comp.std.c++_expires-2001-09-01@nmhq.net (Niklas Matthies)
Date: Fri, 15 Jun 2001 21:35:45 GMT
Raw View
On Fri, 15 Jun 2001 19:23:56 GMT, Michiel Salters <Michiel.Salters@cmg.nl=
> wrote:
> In article <3B27ABC6.B7E670F8@bear.com>, Matvei Brodski says...
[=B7=B7=B7]
> >You can not make the same kind of statement about post-constructed
> >moment. Nothing substantial happens at the time. No compiler help
> >is needed to find out if we reached it.
>=20
> Why not? The post-condition of the post-constructor would be that we ca=
n
> call all "normal" functions safely after that time, which we couldn't d=
o
> before. IF the post-constructor doesn't ADD this guarantee it isn't nee=
ded.
> So a post-constructor would ALWAYS be associated with a logical state
> transition, a capability change. And just as post-constructors are desi=
gned
> to run after the first state transition, post-post-constructors are
> designed to run after this second transition.

One interesting idea would be to be able to associate a set of states
with a class, and to be able to tag member functions as putting the
object into a specific state unless they throw, and tag other member
functions as having to be executed whenever the object changes into some
specific state. There would be a couple of default states, like
under_construction, freshly_constructed, under_deconstruction, so that it
would be possible to tag some member function as always having to be
executed when entering the state freshly_constructed or
under_deconstruction, specifically after the constructor and before the
destructor. Then such a member function could be declared to put the
object into the connected_to_environment state, and so on.
The compiler would automatically generate calls to the appropriate
funtions in the order implied by these declarations.

While I'm pretty sure that something like this won't find its way into
Standard C++ because it supposedly is too major a feature addition for
the benefit, I think some suchlike concept would be the way to go to
more generally solve problems like the one the post-constructor proposal
was aimed at.

-- Niklas Matthies

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Matvei Brodski <mbrodski@bear.com>
Date: Fri, 15 Jun 2001 21:53:48 GMT
Raw View
"Post-constructor" from the scratch:

Let first go a little bit back to a constructor. The idea behind
introduction of a constructor to the language was (correct me, if
I am wrong) to make sure that an object of some class from the
moment of its creation is in some "valid" state. So, when one designs
a class, it is customary to put those initialization steps in the
constructor (Of course, you are not *oblidged* to do so. Instead
you may require your class' users to finish the initialization of
an object - the idea, allow me to repeat, was to give you a way
of making user's life easier, hence - better).

Now, while coding our various constructors we noticed a (minor?)
inconvenience. Namely, due to a object model used to provide for
polymorphism, constructor's writer has a limited use of virtual
functions - since vtable (if used) can be only partially "done"
(if, say, constructor we are "in" is not the one of the most derived
class), virtual functions we are calling will have a static binding.
It is inconvenient, since it prevents us from using, for example, a
Template Function idiom inside a constructor. Yet again, we might
delegate this job to a class' user by providing a way to call
some special function after constructor is done. This, however,
would not make user's life better and his/her code more error prone.

Introduction of a post-constructor can fix the situation.
I.e., post-constructor is going to give us a chance to separate
object initialization into two stages, where in the second stage
we will have a guarantee that the object's *object model* (vtable,
etc.) is fully initialized. In that second stage we may finish
an initialization of the object using whatever language mechanism
we want to.

Now, do we need a post-post-constructor? No, not in the slightest.
You said that when the post-constructor finishes, it is a special
moment too, since now we can call "normal functions", so -
post-post-ctor can run now. The question we need to ask here is
this: What are those "normal" functions? Do we need to call them to
finish object's initialization? If so, we can just call them
*inside* the post-constructor as the last thing it does. If the
answer is no, those functions do not belong to any (post-)*constructor
at all.

Dixi.

Matvei.

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Michiel Salters<Michiel.Salters@cmg.nl>
Date: Wed, 13 Jun 2001 16:21:21 GMT
Raw View
In article <usnh51jfs.fsf@worldnet.att.net>, Thomas A. Horsley says...

>Maybe I should cast this in a completely different light: What I think is
>that the compiler (and absolutely nobody else right now) is the only one
>that knows when an entire object has been completely constructed.  If I want
>to run some code once the object is constructed (and I encounter this
>situation with extreme frequency), then the only completely reliable way to
>make sure that code is always run, is to have a compiler extension to
>support some new way of running code after the object is constructed.
>Hence the name "post constructor".

That's not true, see my posts earlier about using a base class that forces
the most-derived class to be a template, provided by the base class author.

>This is also why I don't know what "post post" constructor means.
>
>There is only one point in time when the object has been fully constructed.

When the constructor of the most derived class has run. And there would be
only one point in time when the object has been fully post-constructed. At
which time post-post-constructors could run. After which there would be only
one point in time ...

Regards,
Michiel Salters


--
Michiel Salters
Consultant Technical Software Engineering
CMG Trade, Transport & Industry
Michiel.Salters@cmg.nl

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: Matvei Brodski <mbrodski@bear.com>
Date: Wed, 13 Jun 2001 18:11:12 GMT
Raw View
Michiel Salters wrote:

> In article <usnh51jfs.fsf@worldnet.att.net>, Thomas A. Horsley says...
>
> >Maybe I should cast this in a completely different light: What I think is
> >that the compiler (and absolutely nobody else right now) is the only one
> >that knows when an entire object has been completely constructed.  If I want
> >to run some code once the object is constructed (and I encounter this
> >situation with extreme frequency), then the only completely reliable way to
> >make sure that code is always run, is to have a compiler extension to
> >support some new way of running code after the object is constructed.
> >Hence the name "post constructor".
>
> That's not true, see my posts earlier about using a base class that forces
> the most-derived class to be a template, provided by the base class author.

Not good enough. The idea is that we want to use a "template method"
pattern in initialization. This way a designer of base class can
make sure that certain steps are made during initialization while
leaving some details to Derived classes for specialization.
And, yes, one can always write something like:

Derived d;
d.post_initialize();

But this is not a solution. Just as

Derived d;
d.initialize();

is a poor substitute for having a constructor.


> >This is also why I don't know what "post post" constructor means.
> >
> >There is only one point in time when the object has been fully constructed.
>
> When the constructor of the most derived class has run. And there would be
> only one point in time when the object has been fully post-constructed.

Then you can easily point out the crucial difference between the
state before and after that point of "object is post-constructed".
Say, when we talk about the point where the object is fully constructed
we can say this:

1.BEFORE object is "fully constructed": vtable (or whatever is used to
provide for polymorphism) is not finalized, so we can not call a
virtual function and expect it to be specialized for the type of
the real object being constructed.
AFTER object is "fully constructed": one can call virtual functions
knowing that the most specialized version will be called.

2.Without a compiler help we can not determine from inside the constructor
body if we reached this point.

You can not make the same kind of statement about post-constructed
moment. Nothing substantial happens at the time. No compiler help
is needed to find out if we reached it.

Matvei.


>

---
[ 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.research.att.com/~austern/csc/faq.html                ]





Author: markw65@my-deja.com (Mark Williams)
Date: Wed, 13 Jun 2001 21:43:59 GMT
Raw View
Michiel Salters<Michiel.Salters@cmg.nl> wrote in message news:<3bFV6.6424$pb1.246801@www.newsranger.com>...
> In article <usnh51jfs.fsf@worldnet.att.net>, Thomas A. Horsley says...
>
> >Maybe I should cast this in a completely different light: What I think is
> >that the compiler (and absolutely nobody else right now) is the only one
> >that knows when an entire object has been completely constructed.  If I want
> >to run some code once the object is constructed (and I encounter this
> >situation with extreme frequency), then the only completely reliable way to
> >make sure that code is always run, is to have a compiler extension to
> >support some new way of running code after the object is constructed.
> >Hence the name "post constructor".
>
> That's not true, see my posts earlier about using a base class that forces
> the most-derived class to be a template, provided by the base class author.
>
> >This is also why I don't know what "post post" constructor means.
> >
> >There is only one point in time when the object has been fully constructed.
>
> When the constructor of the most derived class has run. And there would be
> only one point in time when the object has been fully post-constructed. At
> which time post-post-constructors could run. After which there would be only
> one point in time ...

I think what you are missing, is that there is no change in semantics
between post-constructors and what you term post-post-constructors.
Thus post-post construction can be achieved using the post-constructor
mechanism. On the other hand, there is a significant semantic change
between constructors and post-constructors. Namely, calling a virtual
function in a constructor results in the base class function being
called; in a post constructor it results in the derived class function
being called.

But your argument is essentially correct, only backwards: if we had
post constructors, there would be no need for constructors -
everything that can be achieved using constructors can be achieved
using post constructors.

The argument against this (apart from silently breaking a very small
amount of code) is that the object really isnt a fully operational
derived object until after its been constructed; that's always seemed
a particularly weak argument to me - in the current scheme, it becomes
a derived object as soon as its base classes have finished
constructing, and there is no reason to suppose its a fully
operational derived object at that point either...

Mark Williams

---
[ 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.research.att.com/~austern/csc/faq.html                ]