Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Achieving virtualness from base class constructors

5 views
Skip to first unread message

Scott Meyers

unread,
Jul 2, 2004, 8:40:23 PM7/2/04
to
This is a problem that's been around forever, but I've managed to avoid
thinking about it much until now. It's how do you get the behavior of
virtual functions inside a base class constructor?

As an example, consider a hierarchy for stock transactions, where
type-specific information about each transaction should be written to a log
each time an object is created. Conceptually, this is what I want to do,
though it won't work:

class Transaction {
public:
explicit Transaction() // create transaction, and when done,
{ // call a virtual function to create
... // the type-specific log entry
logTransaction();
}

virtual void logTransaction() const = 0;
};

class BuyTransaction: public Transaction {
public:
virtual void logTransaction() const; // log Buy transactions
};

class SellTransaction: public Transaction {
public:
virtual void logTransaction() const; // log Sell transactions
};

The fly in this ointment is that virtual calls from constructors don't act
virtual. Fine. So how do I get the behavior I want? Things I can think
of:

- Require that each concrete derived class constructor remember to call
logTransaction. Yuck. Easy for somebody to forget to do. This is
really a hierarchy-wide constraint that should be somehow expressed in the
hierarchy base class, IMO.

- Have the base class constructors take one or more parameters from
derived class constructors that, together, allow the base class to make
the appropriate log entry. Yuck. Lots of work on the part of derived
classes, and it may not be practical, because getting the right
information into a member initialization list may cause crazy code
contortions.

- Create proxies for all classes in the hierarchy, have clients use the
proxies, and have each proxy constructor (1) create the object it's a
proxy for and then (2) call logTransaction on the new object. Proxies
might take the form of smart pointers and be generated from one or more
templates. Yuck. It's essentially impossible to create a proxy for a
type that can seamlessly replace the type itself (e.g., you can run
into needing more than one user-defined conversion to make some
function calls succeed), though with enough work, you can reduce the
problems to manageable size. You need to find a way to make sure that
clients use only Proxy<T> objects instead of T objects themselves. And
it seems like a lot of hoops to jump through.

As I said, this is not a new problem. To address it, we have two-phase
construction, whereby clients have to remember to call a member function
after the constructor has run. I've always found this strategy the height
of irony, given that the motivation behind constructors in the first place
was to prevent people from forgetting to call initialization functions, as
they often do in C. People have proposed post-constructors (and even
post-post-constructors) to address this, though they've gone nowhere.

I'm just an author and consultant. I don't actually work for a living.
But presumably many of you do have to work for a living, and in the course
of that work, you've had to address this issue. What do you do? How well
does it work? How would you solve the example problem above?

Thanks,

Scott


[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Carl Barron

unread,
Jul 3, 2004, 6:44:34 AM7/3/04
to
In article <MPG.1b4f973ff...@news.hevanet.com>, Scott Meyers
<Use...@aristeia.com> wrote:

Does logTransaction really need to be virtual or can it be called
after the base class Transaction is constructed and before the
construction of the concrete class is complete? If I understand this
then there is a simple mixture of oop and template programming CRTP.
if this is the case then it is a solution.

class Transaction
{
protected:
Transaction(){}
public:
virtual ~Transaction(){}
// virtual functions not called in construction
};

template <class Derived>
class InBetween:public Transaction
{
public:
InBetween():Transaction()
{
Derived *p = static_cast<Derived *>(this);
p->logTransaction();
}
};

class BuyTransaction:public InBetween<BuyTransaction>
{
//....
public:
void logTransaction(); // non virtual!
}

This requires inheritting from InBetween<concrete_class> which inherits
the base class and the function(s) caled during construction be non
virtual. This approach works on a short test program works with CW9.2
but it might depend on which
class is constructed first.

Pavel Vozenilek

unread,
Jul 3, 2004, 6:48:51 AM7/3/04
to

"Scott Meyers" wrote:

> class Transaction {
> public:
> explicit Transaction() // create transaction, and when done,
> { // call a virtual function to create
> ... // the type-specific log entry
> logTransaction();
> }
>
> virtual void logTransaction() const = 0;
> };
>
> class BuyTransaction: public Transaction {
> public:
> virtual void logTransaction() const; // log Buy transactions
> };
>
> class SellTransaction: public Transaction {
> public:
> virtual void logTransaction() const; // log Sell transactions
> };
>
> The fly in this ointment is that virtual calls from constructors don't act
> virtual.
>

CRTP?

template<class ActualType>
class Transaction
{
public:
explicit Transaction() {
ActualType::logTransaction();
}
};

class BuyTransaction: public Transaction<BuyTransaction>
{
....
};

/Pavel

Antoun Kanawati

unread,
Jul 3, 2004, 11:25:20 AM7/3/04
to
Scott Meyers wrote:

> This is a problem that's been around forever, but I've managed to avoid
> thinking about it much until now. It's how do you get the behavior of
> virtual functions inside a base class constructor?

[snip]

> I'm just an author and consultant. I don't actually work for a living.
> But presumably many of you do have to work for a living, and in the course
> of that work, you've had to address this issue. What do you do? How well
> does it work? How would you solve the example problem above?

The problem is that you want to access the complete state of the object
while that state is undefined. Mechanically, the virtual function table
is not properly hooked up until after construction is complete.
Theoretically, inside the constructor you have a partial view of a
partially defined object, and the constructor's task is to make that
partial view well defined, while polymorphic dispatch presumes a fully
defined object.

Polymorphic behavior during construction is just weird, and any solution
that tries to retain that flavor will show signs of weirdness. You have
made a good case for this statement in the part that I snipped.

A "paradigm shift" is needed here; something consultants and authors are
best at :) For "real" programmers, a different decomposition of the
problem may provide a less unnatural set of requirements.

--
ak

Dave Harris

unread,
Jul 3, 2004, 11:27:21 AM7/3/04
to
Use...@aristeia.com (Scott Meyers) wrote (abridged):

> This is a problem that's been around forever, but I've managed to avoid
> thinking about it much until now. It's how do you get the behavior of
> virtual functions inside a base class constructor?

It's a slightly strange thing to want to do. If you do need it, one
approach is to pass a call-back function to the base constructor. Another,
probably easier approach is to use some kind of factory.

template <typename Transaction>
Transaction *makeTransaction() {
Transaction *p = new Transaction();
p->logTransaction();
return p;
}

You can use a base class and virtual functions instead of a template if
you prefer.

class TransactionFactory {
public:
Transaction *makeTransaction() {
Transaction *p = newTransaction();
p->logTransaction();
return p;
}
private:
virtual Transaction *newTransaction() {
return new Transaction;
}
};

It's an example of something which should be easier with virtual static
functions or some other metaclass system, because you wouldn't need the
separate TransactionFactory class. You would be able to make the
Transaction constructor protected without needing to use "friend".

This is similar to what you describe as "proxies", except without the
proxies. It's also similar to:

> - Require that each concrete derived class constructor remember to
> call logTransaction. Yuck. Easy for somebody to forget to do.
> This is really a hierarchy-wide constraint that should be somehow
> expressed in the hierarchy base class, IMO.

in that both require cooperation from the derived class. The derived class
should not declare a constructor which by-passes the logging. You can
enforce that by requiring the base constructor to take a token which only
the factory can create.

-- Dave Harris, Nottingham, UK

Scott Meyers

unread,
Jul 3, 2004, 9:56:07 PM7/3/04
to
On 3 Jul 2004 06:44:34 -0400, Carl Barron wrote:
> Does logTransaction really need to be virtual or can it be called
> after the base class Transaction is constructed and before the
> construction of the concrete class is complete?

In the general case, logTransaction needs access to all the local data
members in its class, so it needs to be called, at the earliest, after
member initialization of all the derived class data members.

Scott

Alf P. Steinbach

unread,
Jul 3, 2004, 9:58:42 PM7/3/04
to
* Carl Barron:

> In article <MPG.1b4f973ff...@news.hevanet.com>, Scott Meyers
> <Use...@aristeia.com> wrote:
>
> > This is a problem that's been around forever, but I've managed to avoid
> > thinking about it much until now. It's how do you get the behavior of
> > virtual functions inside a base class constructor?

FAQ 23.4.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

Scott Meyers

unread,
Jul 3, 2004, 10:03:05 PM7/3/04
to
On 3 Jul 2004 06:48:51 -0400, Pavel Vozenilek wrote:
> CRTP?
>
> template<class ActualType>
> class Transaction
> {
> public:
> explicit Transaction() {
> ActualType::logTransaction();

As with the solution proposed by Carl Barron, this invokes logTransaction
in the derived class before the derived class data members have been
initialized. The danger of such uninitialized variables is why virtual
functions don't go down into derived classes during base class construction
in the first place.

Scott

Scott Meyers

unread,
Jul 3, 2004, 10:04:00 PM7/3/04
to
On 3 Jul 2004 11:25:20 -0400, Antoun Kanawati wrote:
> Polymorphic behavior during construction is just weird, and any solution
> that tries to retain that flavor will show signs of weirdness. You have
> made a good case for this statement in the part that I snipped.
>
> A "paradigm shift" is needed here; something consultants and authors are
> best at :) For "real" programmers, a different decomposition of the
> problem may provide a less unnatural set of requirements.

Regarding weirdness and paradigm shift, I'll just remark that my
understanding is that in both Java and C#, the virtual call from the base
class constructor resolves to the derived class. At that point, however,
data members in the derived class have been only default-initialized (to
null and 0 in C#, perhaps the same in Java, I don't know). So different
languages approach virtualness during construction in different ways.

Regarding a different decomposition, I welcome ideas on how to achieve the
behavior I described in my original post. Is it really so odd to want to
perform something like type-specific logging each time an object in a
hierarchy is created?

Scott

Scott Meyers

unread,
Jul 3, 2004, 10:06:48 PM7/3/04
to
On 3 Jul 2004 11:27:21 -0400, Dave Harris wrote:
> It's a slightly strange thing to want to do.

As I wrote to another poster, is it really so odd to want to perform


something like type-specific logging each time an object in a hierarchy is

created? Frankly, this sounds to me like one of those cross-cutting
concerns that AOP seeks to address, but that's probably an entirely
separate thread.

> If you do need it, one
> approach is to pass a call-back function to the base constructor.

I thought about that, but what can the call-back do? The object to be
logged isn't fully constructed at the point where the base class
constructor executes.

> Another,
> probably easier approach is to use some kind of factory.
>
> template <typename Transaction>
> Transaction *makeTransaction() {
> Transaction *p = new Transaction();
> p->logTransaction();
> return p;
> }

Interesting idea, thanks.

> This is similar to what you describe as "proxies", except without the
> proxies. It's also similar to:
>
> > - Require that each concrete derived class constructor remember to
> > call logTransaction. Yuck. Easy for somebody to forget to do.
> > This is really a hierarchy-wide constraint that should be somehow
> > expressed in the hierarchy base class, IMO.
>
> in that both require cooperation from the derived class. The derived class
> should not declare a constructor which by-passes the logging. You can
> enforce that by requiring the base constructor to take a token which only
> the factory can create.

Very clever. Again, thanks.

Scott

Balog Pal

unread,
Jul 3, 2004, 10:10:26 PM7/3/04
to
"Scott Meyers" <Use...@aristeia.com> wrote in message
news:MPG.1b4f973ff...@news.hevanet.com...

> The fly in this ointment is that virtual calls from constructors don't act
> virtual. Fine.

You would not be mush ahead even if they acted virtual. As mostly you pass
params to the ctor [of the most derived class] that are used to create the
initial state. If you want to log, sure you want to log the final state. If
you called to the final function, like Carl Barron's solution in another
post the function would face uninited, or even unconstructed member
variables to work with. Sending the program to the UB territory.

>So how do I get the behavior I want?

For the concrete example (logging) I'd likely pass a cooked string or struct
as a parameter to the base class ctor. That prevents the 'I forgot' stuff.
The base class may provide some helpers.

Though as that ctor is called too early itis far from perfect and likely
force some duplication somewhere.

If C++ were a little different, and member init hapened in the order of
member init clausules some of it could be avoided -- as we have it I see no
easy ways.

Paul

Dave Harris

unread,
Jul 5, 2004, 5:54:55 AM7/5/04
to
Use...@aristeia.com (Scott Meyers) wrote (abridged):
> As I wrote to another poster, is it really so odd to want to perform
> something like type-specific logging each time an object in a hierarchy
> is created?

As written, you seemed to be trying to do it just before it was fully
created. The problem seems to be one of logic, rather than programming
language.

Also, instance creation is a concept of the programming domain. As such it
probably doesn't correspond to anything in the problem domain. If it does
correspond to something in the problem domain, that suggests to me there
may be a better place to put the logging, eg when the object is added to
some database, or when its existence otherwise becomes known to the world.


> I thought about that, but what can the call-back do? The object to be
> logged isn't fully constructed at the point where the base class
> constructor executes.

It can't do much with the object being constructed, but it can do other
stuff, such as log the name of the class. If you use a functor instead of
a function, you can have it collect the constructor arguments and use
those too. (In other words, use a second class. Many solutions amount to
having two classes instead of one.)

-- Dave Harris, Nottingham, UK

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Antoun Kanawati

unread,
Jul 5, 2004, 5:58:57 AM7/5/04
to
Scott Meyers wrote:
> Regarding weirdness and paradigm shift, I'll just remark that my
> understanding is that in both Java and C#, the virtual call from the base
> class constructor resolves to the derived class. At that point, however,
> data members in the derived class have been only default-initialized (to
> null and 0 in C#, perhaps the same in Java, I don't know). So different
> languages approach virtualness during construction in different ways.

Options that simulate C#/Java behavior have already been proposed on
this thread, and the caveat of accessing a partially defined object
was noted.

> Is it really so odd to want to
> perform something like type-specific logging each time an object in a
> hierarchy is created?

[I've rearranged the sequence a bit, I hope you don't mind.]

Nothing really odd with the requirement itself. The oddity is with the
proposed interpretation: invoking subtype behavior in a parent
constructor in C++. "Every time an object is created" is a loose
phrase with a wide range of interpretations. It may mean as soon
as the object creation process starts, or as soon as the object
creation is finished, or somewhere in between. If you consider Genesis
as a model: in the beginning, you have only the Word; at the end you
have light, water, beasts, and [intelligent?] human beings. These
are vastly different contexts.

<digression>
Personally, I find the C# and Java behavior in poor taste; it
means that all member functions have to be written against partially
initialized objects, which can quickly lead to (1) a seriously warped
state of mind, and/or (2) very few people paying attention to the
possibility, thereby creating interesting spaces for bugs to live
and breed in. Of course, the issue can be addressed with boiler-plate
fine print in Design-By-Contract statements :) "assert(nothing is null)"
and that sort of stuff.
</digression>

> Regarding a different decomposition, I welcome ideas on how to achieve the
> behavior I described in my original post.

The first thing that came to my mind was the factory approach (already
shown on this thread); something like the following, or its variations:

template<typename TransactionType>
void make_txn(smart_pointer<TransactionType> &foo) {
foo = smart_pointer<TransactionType>(new TransactionType());
foo->logTransaction();
}

But, you've already yucked that sort of option [post-constructor, smart
pointer, proxy-like].

For the problem you've proposed, logging type-specific information
during parent construction, we need to consider the nature of that
information.

If it is strictly type-specific, i.e.: not related to
the state of the object being constructed, it would be reasonable
to relegate that information to a separate class; e.g.:

class Transaction {
protected:
// SubType(arg...) : Transaction(this, ...), .. { }
template<SubType>
Transaction(SubType *, ...) : ... {
....
Logger<SubType> logger;
logger.logTransaction(...);
}
};

If you need more information than the type, a logger hierarchy
may be in order. If, for example, it is necessary to use some
parts of the subtype's ctor argument list, the following approaches
may be viable:

class Logger { public: virtual void logTransaction() =0; };

// subtype(arg1, arg2, ...)
// : Transaction(LoggerSubtype(arg1, arg2)), ...
Transaction(const Logger &logger, ...) : ... {
....
logger.logTransaction();
}

// subtype(arg1, arg2, ...)
// : Transaction(new LoggerSubtype(arg1, arg2)), ...
Transaction(SmartPtr<Logger> logger, ...) : ... {
....
logger->logTransaction();
}

At the extreme end of this range, you may need ALL the information
in the instance of the subtype to satisfy the requirement. For that,
it is a reasonable idea to have two kinds of classes: one to store
the representation, and another to provide interesting behavior
(anything other than set/get). We interpret the original requirement
to mean "when a behavior object is created", and program accordingly.

I hope you find some of these ideas interesting.
--
ak

Matthias Hofmann

unread,
Jul 5, 2004, 6:02:15 AM7/5/04
to
Scott Meyers <Use...@aristeia.com> schrieb in im Newsbeitrag:
MPG.1b507de61...@news.hevanet.com...

[snip]

> Regarding a different decomposition, I welcome ideas on how to achieve the
> behavior I described in my original post. Is it really so odd to want to
> perform something like type-specific logging each time an object in a
> hierarchy is created?

In another post you have said that two-phase initialization defeated the
purpose of constructors, as they were meant to prevent people from
forgetting to call initialization functions. However, logTransaction() does
not really do any initialization, so it does not conceptually belong into
the constructor.

But what about this one:

class Transaction
{
public:
class LogInfo
{
virtual INFO GetInfo() const = 0;
};

Transaction( const LogInfo& info )
{
// Use info.GetInfo() to log transaction.
}
};

class BuyTransaction : public Transaction
{
public:

class BuyInfo : public Transaction::LogInfo
{
virtual INFO GetInfo() const;
};

BuyTransaction() : Transaction( BuyInfo() ) {}
};

Just an idea... ;-)

Matthias

ka...@gabi-soft.fr

unread,
Jul 5, 2004, 3:14:30 PM7/5/04
to
Scott Meyers <Use...@aristeia.com> wrote in message
news:<MPG.1b4f973ff...@news.hevanet.com>...

> This is a problem that's been around forever, but I've managed to


> avoid thinking about it much until now. It's how do you get the
> behavior of virtual functions inside a base class constructor?

You don't:-).

> As an example, consider a hierarchy for stock transactions, where
> type-specific information about each transaction should be written to
> a log each time an object is created. Conceptually, this is what I
> want to do, though it won't work:

> class Transaction {
> public:
> explicit Transaction() // create transaction, and when done,
> { // call a virtual function to create
> ... // the type-specific log entry
> logTransaction();
> }

> virtual void logTransaction() const = 0;
> };

> class BuyTransaction: public Transaction {
> public:
> virtual void logTransaction() const; // log Buy transactions
> };

> class SellTransaction: public Transaction {
> public:
> virtual void logTransaction() const; // log Sell transactions
> };

> The fly in this ointment is that virtual calls from constructors don't act
> virtual.

The fly in this ointment is that when you want to call the virtual
function, the data that it needs probably hasn't been initialized yet.
Different languages handle this differently: in C++, you get whatever
the implementation gives you, typically something like "Pure virtual
function called" and a core dump. In Java, you get
NullPointerException. And so on.

In none of the languages that I know does it actually work. What you
need is some sort of post-constructor.

> Fine. So how do I get the behavior I want? Things I can think of:

[I've cut them, since you pointed out yourself why they don't
work...]

> I'm just an author and consultant. I don't actually work for a
> living. But presumably many of you do have to work for a living, and
> in the course of that work, you've had to address this issue. What do
> you do? How well does it work? How would you solve the example
> problem above?

First of all, in real programs, the problem doesn't seem to occur all
that often, and generally, when it does, it concerns entity classes,
with use reference semantics (passed by pointers or references, etc.,
because identity is important). In such cases, a factory method is the
obvious solution; the user never invokes new to obtain an object, but
rather invokes a factory method which invokes new, then calls the
method. Now that we have member templates, this can easily be solved by
means of a member template in the base class:

template< typename Derived >
Transaction* Transaction::create() {
Transaction* result = new Derived ;
result->logTransaction() ;
return result ;
}

It's still limiting -- you can't have different derived classes whose
constructors require different types of parameters, for example. But
it's better than nothing.

If you do need different types of parameters, and systematically, in the
old days, you left it up to each derived class to implement the correct
factory method. Superficially, that solution looks a lot like the one
simply requiring the derived class constructor to call the function, but
psychologically, it seems to work a lot better.

Finally, in the one case where I did want to be able to create the class
on the stack, I used a "smart parameter". In my case, the class
actually took a string parameter, which was passed to the function to be
called after construction. However, I wrapped this parameter in a Proxy
class, so that the actual constructor declaration looked something like:

FieldArray::FieldArray( FieldArrayInitializer const& init ) ;

FieldArrayInitializer had a constructor which took a string (and
constructors for all types which converted implicitly into string), and
called the necessary function in its destructor. (There was a small
hack in the constructor of FieldArray in order for it to know who to
call the function on.) If the class didn't need a parameter otherwise,
of course, it would be trivial to create a dummy parameter with a
default initializer, e.g.:

Base::Base( Initializer const& init = Initializer() ) ;

Note that this solution doesn't work if the type can be used as a
temporary variable. The destructor of the initializer class gets called
too late. Although it just occured to me that this problem is easily
avoided. The derived classes take a string as parameter, and invoke the
base class destructor as:

DerivedFieldArray::DerivedFieldArray(
std::string const& s )
: FieldArray( Initializer( s ) )
{
}

This guarantees that the destructor of the Initializer is called on
leaving the constructor of the derived class. It does introduce a
constraint on the derived class, but one that is pretty hard to forget.

Again: FieldArray is a friend of Initializer. The constructor of
FieldArray sets a pointer to itself in the Initializer, and the
destructor of the Initializer calls the desired function through this
pointer in its destructor.


Most of the time today, of course, I'll use delegation. This is what my
current implementation of FieldArray does, for example. The derived
class must furnish a delegate to the base classes constructor which will
work correctly. It is the delegate which contains the virtual function
you want to call. And of course, the delegate is fully constructed by
the derived class before calling the base class constructor.

If the delegate is not needed beyond the end of the constructor, a
temporary instance on the stack can be used, exactly like the case for
the initializer, above. And if the class is using the compilation
firewall idiom, it should be possible to merge the delegate and the
implementation class into one.

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Andrei Alexandrescu (See Website for Email)

unread,
Jul 6, 2004, 5:19:04 PM7/6/04
to
[snipoletto]

> This guarantees that the destructor of the Initializer is called on
> leaving the constructor of the derived class. It does introduce a
> constraint on the derived class, but one that is pretty hard to forget.

[snipolatto]

It also guarantees that the destructor of the Initializer is called if the
derived class' constructor fails by throwing an exception, which makes this
approach rather useless.

Andrei

ka...@gabi-soft.fr

unread,
Jul 7, 2004, 1:52:48 PM7/7/04
to
"Andrei Alexandrescu \(See Website for Email\)"
<SeeWebsit...@moderncppdesign.com> wrote in message
news:<2kvugbF...@uni-berlin.de>...

> [snipoletto]
> > This guarantees that the destructor of the Initializer is called on
> > leaving the constructor of the derived class. It does introduce a
> > constraint on the derived class, but one that is pretty hard to forget.

> [snipolatto]

> It also guarantees that the destructor of the Initializer is called if
> the derived class' constructor fails by throwing an exception, which
> makes this approach rather useless.

Good point. I developed this approach long before I had access to
compilers which supported exceptions, and haven't really looked at it
much since. In the one case I actually used it, the constructors
couldn't throw in practice, but this was more by accident -- I hadn't
considered the problem.

Given that the classes in question know about the temporary, and are
friend to it, they could systematically set some sort of flag if they
threw. But that sort of takes away most of the interest; the controling
code becomes quite complex, the constructors probably need a try block,
etc., etc.

Note that the problem is only really present if the derived class
constructors can throw. The base class can simply defer setting the
pointer to itself until it has finished anything which can throw (and
the destructor of the temporary definitely checks this pointer before
calling the function).

--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Luís Pedro Coelho

unread,
Jul 7, 2004, 9:50:45 PM7/7/04
to
On 3 Jul 2004 06:48:51 -0400, Pavel Vozenilek wrote:
> CRTP?

Scott Meyers wrote:
> The danger of such uninitialized variables is why virtual
> functions don't go down into derived classes during base class
> construction in the first place.


Except, in this case, there is probably no need for the function to be a
member function, a class static will probably do it:

template<typename Child>
class Transaction {
Transaction() {
Child::log();
....
}
};


class MySpecialTransaction: public Transaction<MySpecialTransaction> {
static void log() {
...
}
};

And this can be made better if there is a need for *base class members*:


template <typename Child>
struct Transaction {
Transaction(): id (get_id()) {
Child::log(this);
....
}

protected:
transaction_id_type id;
};

struct MyTransaction : public Transaction<MyTransaction> {
static void log(Transaction<MyTransaction>* this_) {
logstream() << "Created MyTransaction " << this_->id << std::endl;
}
};

What do you think?

luisp
http://luispedro.org

Andrei Alexandrescu (See Website for Email)

unread,
Jul 7, 2004, 9:53:33 PM7/7/04
to
<ka...@gabi-soft.fr> wrote in message
news:d6652001.04070...@posting.google.com...

> "Andrei Alexandrescu \(See Website for Email\)"
> <SeeWebsit...@moderncppdesign.com> wrote in message
> news:<2kvugbF...@uni-berlin.de>...
> > [snipoletto]
> > > This guarantees that the destructor of the Initializer is called on
> > > leaving the constructor of the derived class. It does introduce a
> > > constraint on the derived class, but one that is pretty hard to
forget.
>
> > [snipolatto]
>
> > It also guarantees that the destructor of the Initializer is called if
> > the derived class' constructor fails by throwing an exception, which
> > makes this approach rather useless.
>
> Good point. I developed this approach long before I had access to
> compilers which supported exceptions, and haven't really looked at it
> much since. In the one case I actually used it, the constructors
> couldn't throw in practice, but this was more by accident -- I hadn't
> considered the problem.

One solution I have combines simplicity (desirable) with asking for
cooperation from the implementer of derived classes (undesirable).

class B { // hierarchy root
public:
B(some_args, const std::type_info& mostDerived = typeid(B))
{
.
if (mostDerived == typeid(B)) PostInitialize();
}
protected:
void PostInitialize() { . } // virtual not needed
};

class D : public B { // some class in the hierarchy
D(some_args, const std::type_info& mostDerived = typeid(D))
: B(some_args, mostDerived)
{
.
if (mostDerived == typeid(D)) PostInitialize();
}
protected:
void PostInitialize() { . } // virtual not needed
}

Basically each class in the hierarchy takes a typeid as an extra argument
and adds one line at the end of the constructor. The approach can be
extended in the obvious ways (replace the typeid with some templated
protected type that has a PostCtor() method etc.

I'd be curious on how this could be improved beyond factory methods, storing
extra state, or requiring cooperation from the programmer of subclasses.


Andrei

Shannon Barber

unread,
Jul 7, 2004, 10:01:17 PM7/7/04
to
Antoun Kanawati <ant...@comcast.net> wrote in message news:<LowFc.16185$MB3.8965@attbi_s04>...

> Scott Meyers wrote:
>
> > This is a problem that's been around forever, but I've managed to avoid
> > thinking about it much until now. It's how do you get the behavior of
> > virtual functions inside a base class constructor?
>
> [snip]
>
> > I'm just an author and consultant. I don't actually work for a living.
> > But presumably many of you do have to work for a living, and in the course
> > of that work, you've had to address this issue. What do you do? How well
> > does it work? How would you solve the example problem above?
>
> The problem is that you want to access the complete state of the object
> while that state is undefined. Mechanically, the virtual function table
> is not properly hooked up until after construction is complete.
> Theoretically, inside the constructor you have a partial view of a
> partially defined object, and the constructor's task is to make that
> partial view well defined, while polymorphic dispatch presumes a fully
> defined object.

I think that's a fundamental C++ problem; shouldn't the vtbl be
completely constructed prior to the execution of any ctor code? It
would make object construction a two-stage process (only for
'virtualized' classes), but I think it would be worth it. Does
someone with more clout than me want to submit it for C++0x?

Antoun Kanawati

unread,
Jul 8, 2004, 8:01:47 AM7/8/04
to
Shannon Barber wrote:
> I think that's a fundamental C++ problem; shouldn't the vtbl be
> completely constructed prior to the execution of any ctor code? It
> would make object construction a two-stage process (only for
> 'virtualized' classes), but I think it would be worth it.

The vtable is usually "constructed" at compile time. It's only
a pointer that changes as construction walks down the inheritance
chain.

The way C++ does it makes sense. It makes behavior predictable
during construction. The last thing you want is your object
behaving like a Derived while it hasn't acheived full Base-ness
yet. I know that it makes Scott's little problem hard to solve,
but it makes a whole batch of other much more common and serious
problems go away.

Besides, there are serious implications of hooking up the vtable
before any construction takes place: it means you want to allow
derived implementations of functions to be callable even before
the object is fully constructed; therefore, you must define some
concept of coherent state for the object before it is even
constructed. So, now you have to define two kinds of construction:
pre-construction, which is always compiler generated to makes the
object barely usable, and plain-old-construction as we know it and
love it.

Try defining a coherent concept of unbound reference. In the
current C++, they don't even exist.

I hope you're not suggesting that we hook up the final vtable,
and leave the rest of the object as a random collection of bits.

Even with a sink-or-swim frame of mind, this sort of stuff is too
evil.

> Does someone with more clout than me want to submit it for C++0x?

Hey, at least offer this person a free beer, or a dinner. Approaching
clout has certain protocols, you know.
--
Antoun Kanawati
antou...@comcast.dot.net
[remove .dot and .at before use]

Francis Glassborow

unread,
Jul 8, 2004, 7:23:12 PM7/8/04
to
In article <de001473.04070...@posting.google.com>, Shannon
Barber <shannon...@myrealbox.com> writes

>I think that's a fundamental C++ problem; shouldn't the vtbl be
>completely constructed prior to the execution of any ctor code? It
>would make object construction a two-stage process (only for
>'virtualized' classes), but I think it would be worth it. Does
>someone with more clout than me want to submit it for C++0x?

Looks simple doesn't it? Actually it is very far from being so. By the
time we have multiple inheritance and virtual base classes in the mix it
becomes a nightmare to even understand all the alternatives and their
implications.

However putting that to one side, the problem actually has little to do
with any putative vtbl but has to do with the initialisation of member
data. What we have today guarantees that any well-designed polymorphic
concrete class will behave correctly as a base class. What you propose
breaks that guarantee:

class base {
int i;
public:
base(): i(0) {printon(clog);}
virtual void printon(ostream & out){out << i <<'\n';}
};

class derived: public base {
std::string message;
public:
derived():message("Help!"){};
void printon(ostream & out){out << i <<", " << message << '\n';}
};

Under current rules those definitions are (at least in the context of
what we are discussing) OK. Change the rule to the one you are
advocating and we have UB.

In simple terms, even if we wanted to, we could not contemplate the such
a change.


--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects

Bart van Ingen Schenau

unread,
Jul 8, 2004, 7:23:34 PM7/8/04
to
On 6 Jul 2004 17:19:04 -0400, "Andrei Alexandrescu \(See Website for
Email\)" <SeeWebsit...@moderncppdesign.com> wrote:

>[snipoletto]
>> This guarantees that the destructor of the Initializer is called on
>> leaving the constructor of the derived class. It does introduce a
>> constraint on the derived class, but one that is pretty hard to forget.
>
>[snipolatto]
>
>It also guarantees that the destructor of the Initializer is called if the
>derived class' constructor fails by throwing an exception, which makes this
>approach rather useless.

Can't you write the destructor of Initialiser like this:

Initialiser::~Initialiser()
{
if (!uncaught_exception())
{
// Perform normal (post-constructor) actions
}
// else
// Stack unwinding due to exception in progress.
}

>
>Andrei
>
Bart v Ingen Schenau

Robert Kindred

unread,
Jul 8, 2004, 7:33:25 PM7/8/04
to

"Scott Meyers" <Use...@aristeia.com> wrote in message
news:MPG.1b4f973ff...@news.hevanet.com...
> This is a problem that's been around forever, but I've managed to avoid
> thinking about it much until now. It's how do you get the behavior of
> virtual functions inside a base class constructor?
>
If you follow the ACE philosophy
(http://www.huihoo.org/ace_tao/ACE-5.2+TAO-1.2/ACE_wrappers/docs/ACE-guideli
nes.html), you don't do much in the constructor anyway. Do only safe
things, like zero out scalars, null out pointers, set bools, etc.
Constructors that fail make objects that are little better than zombies, and
clutter up memory in programs that must run for long periods of time. The
important stuff is done in open() and close(). Your logging could be done
there.

<snip>


>
> I'm just an author and consultant. I don't actually work for a living.
> But presumably many of you do have to work for a living, and in the course
> of that work, you've had to address this issue. What do you do? How well
> does it work? How would you solve the example problem above?
>
> Thanks,
>
> Scott
>

hth, Robert Kindred

Nicola Musatti

unread,
Jul 8, 2004, 7:57:15 PM7/8/04
to
shannon...@myrealbox.com (Shannon Barber) wrote in message news:<de001473.04070...@posting.google.com>...

> Antoun Kanawati <ant...@comcast.net> wrote in message news:<LowFc.16185$MB3.8965@attbi_s04>...
[...]

> > The problem is that you want to access the complete state of the object
> > while that state is undefined. Mechanically, the virtual function table
> > is not properly hooked up until after construction is complete.
> > Theoretically, inside the constructor you have a partial view of a
> > partially defined object, and the constructor's task is to make that
> > partial view well defined, while polymorphic dispatch presumes a fully
> > defined object.
>
> I think that's a fundamental C++ problem; shouldn't the vtbl be
> completely constructed prior to the execution of any ctor code? It
> would make object construction a two-stage process (only for
> 'virtualized' classes), but I think it would be worth it. Does
> someone with more clout than me want to submit it for C++0x?

Allowing virtual functions to act upon the non initialized portion of
the most derived class instance? In my opinion this is one thing C++
got right, in contrast with other languages (Delphi comes to mind)
which forsake the base/derived class boundary in order to allow
virtual constructors.

Cheers,
Nicola Musatti

Andrei Alexandrescu (See Website for Email)

unread,
Jul 9, 2004, 9:16:02 AM7/9/04
to
"Bart van Ingen Schenau" <Bart.van.In...@ict.nl> wrote in message
news:i68qe0t81q8v5ga45...@4ax.com...

> On 6 Jul 2004 17:19:04 -0400, "Andrei Alexandrescu \(See Website for
> Email\)" <SeeWebsit...@moderncppdesign.com> wrote:
>
> >[snipoletto]
> >> This guarantees that the destructor of the Initializer is called on
> >> leaving the constructor of the derived class. It does introduce a
> >> constraint on the derived class, but one that is pretty hard to forget.
> >
> >[snipolatto]
> >
> >It also guarantees that the destructor of the Initializer is called if
the
> >derived class' constructor fails by throwing an exception, which makes
this
> >approach rather useless.
>
> Can't you write the destructor of Initialiser like this:
>
> Initialiser::~Initialiser()
> {
> if (!uncaught_exception())
> {
> // Perform normal (post-constructor) actions
> }
> // else
> // Stack unwinding due to exception in progress.
> }

No. Find cover, I can hear the hordes of furious posters coming :o).

Andrei

Andrei Alexandrescu (See Website for Email)

unread,
Jul 9, 2004, 9:18:58 AM7/9/04
to
"Nicola Musatti" <Nicola....@ObjectWay.it> wrote in message
news:a327cf48.04070...@posting.google.com...

> Allowing virtual functions to act upon the non initialized portion of
> the most derived class instance? In my opinion this is one thing C++
> got right, in contrast with other languages (Delphi comes to mind)
> which forsake the base/derived class boundary in order to allow
> virtual constructors.

Ideally C++ would allow a function to be called just after the constructor
of the whole object completes. That would be the best of both worlds.

Andrei

Gerhard Menzl

unread,
Jul 9, 2004, 9:32:20 AM7/9/04
to
Robert Kindred wrote:

> If you follow the ACE philosophy
> (http://www.huihoo.org/ace_tao/ACE-5.2+TAO-1.2/ACE_wrappers/docs/ACE-guideli
> nes.html), you don't do much in the constructor anyway. Do only safe
> things, like zero out scalars, null out pointers, set bools, etc.
> Constructors that fail make objects that are little better than zombies, and
> clutter up memory in programs that must run for long periods of time. The
> important stuff is done in open() and close(). Your logging could be done
> there.

How so? Constructors that fail throw and kill the object in mid-birth.
No zombie state, no memory hogging. It is secondary initialization and
deinitialization functions like open() and close() that cause half-baked
objects with funny extra states. Unless the problem domain explicitly
calls for it, such design is best avoided.

Remember that ACE was designed long before exceptions became mainstream,
and there was no real alternative for two-phase construction.

--
Gerhard Menzl

Humans may reply by replacing the obviously faked part of my e-mail
address with "kapsch".

I V

unread,
Jul 9, 2004, 10:09:34 AM7/9/04
to
On Thu, 08 Jul 2004 19:33:25 -0400, Robert Kindred wrote:

> "Scott Meyers" <Use...@aristeia.com> wrote in message
> news:MPG.1b4f973ff...@news.hevanet.com...
>> This is a problem that's been around forever, but I've managed to avoid
>> thinking about it much until now. It's how do you get the behavior of
>> virtual functions inside a base class constructor?
>>
> If you follow the ACE philosophy
> (http://www.huihoo.org/ace_tao/ACE-5.2+TAO-1.2/ACE_wrappers/docs/ACE-guideli
> nes.html), you don't do much in the constructor anyway. Do only safe

Hmm... the document you reference only recommends not doing much in
constructors as a workaround for lack of exception handling. I may be
wrong, but the general impression I get is that the current recommendation
is to initialise your objects as much as possible in the constructor, to
avoid forgetting to initialise objects before they're used.

> things, like zero out scalars, null out pointers, set bools, etc.
> Constructors that fail make objects that are little better than zombies,
> and clutter up memory in programs that must run for long periods of
> time. The

It's the other way round, surely? When constructors fail (that is, throw
an exception, as that's the only way constructors _can_ fail), the object
gets deleted. Objects that don't do much initialising in the constructor
but fail in their 'open' method will hang around until they get manually
deleted (or go out of scope).

--
" - Penny, I worry that you are loosing heart... You are not the sweet little
girl I once knew. Where's your sense of wonder?
- Currently flowing into a sanitary napkin... Guess where my childlike
innocence and idle dreams are currently wedged. Come on, I dare you."
http://www.huh.34sp.com/

Alf P. Steinbach

unread,
Jul 9, 2004, 10:12:01 PM7/9/04
to
* Andrei Alexandrescu (See Website for Email):

> "Nicola Musatti" <Nicola....@ObjectWay.it> wrote in message
> news:a327cf48.04070...@posting.google.com...
> > Allowing virtual functions to act upon the non initialized portion of
> > the most derived class instance? In my opinion this is one thing C++
> > got right, in contrast with other languages (Delphi comes to mind)
> > which forsake the base/derived class boundary in order to allow
> > virtual constructors.
>
> Ideally C++ would allow a function to be called just after the constructor
> of the whole object completes. That would be the best of both worlds.

C++ already allows that; it just doesn't do it for you.

And it's not at all a good solution: it leads to degenerate code such as
in MFC where all real construction is done in that post-constructor-call
function. And that means that derived class code may and by Murphy's law
_will_ be executed before even the base class' class invariant has been
established, breaking type-safety (especially ironic that the standard
library uses such technique and documents it, as I recall, "if the object
is destroyed before it has been initialized ... the effect is undefined").

I think the reason people want this horror is because they don't know or
understand better, but you of all people should (posting in haste?).
Techniques: FAQ item 23.4. It's not as if it's very difficult in any way.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

David Ferguson

unread,
Jul 9, 2004, 10:28:59 PM7/9/04
to
> Scott Meyers wrote in message
> As I said, this is not a new problem. To address it, we have two-phase
> construction, whereby clients have to remember to call a member function
> after the constructor has run.

Can you automate the two-phase construction with a template class? All
the classes in your hierarchy should have protected constructors. Clients
can only create Logger<XxxTransaction> types. For example:

#include <iostream>
#include <string>

using namespace std;

template <class T>
class Logger : public T
{
public:
Logger () : T() { T::logTransaction(); }
};

class Transaction {
public:
virtual ~Transaction() {}

explicit Transaction() {}



virtual void logTransaction() const = 0;
};

class BuyTransaction: public Transaction
{
public:

virtual void logTransaction() const { cout << "buy\n"; }

protected:
BuyTransaction () {}


};

class SellTransaction: public Transaction
{
public:

virtual void logTransaction() const { cout << "sell\n"; }

protected:
SellTransaction () {}
};

int main (int argc, char* argv[])
{
// BuyTransaction xxx; can't accidentally do this
// since there is no access to the ctor

Logger<BuyTransaction > one = Logger<BuyTransaction>();
Logger<SellTransaction> two = Logger<SellTransaction>();

return 0;
}


You could probably make this more generic for any class that needs a post
constructor initialization routine. Assuming the name of the post
constructor initialization member function is 'Init()' this template
should work:

template <class T>
class Init : public T
{
public:
Init () : T() { T::Init(); }

template <class A1>
Init (const A1& a1) : T(a1) { T::Init(); }

template <class A1, class A2>
Init (const A1& a1, const A2& a2) : T(a1, a2) { T::Init(); }

// ... repeat as necessary ...
};




> and in the course
> of that work, you've had to address this issue. What do you do?

I haven't had to do this yet in a work environment, but it seems usable
(simple enough to remember how it works and why I did it :) ).

Regards David

Pavel Vozenilek

unread,
Jul 9, 2004, 10:32:44 PM7/9/04
to

"Andrei Alexandrescu wrote:

> Ideally C++ would allow a function to be called just after the constructor
> of the whole object completes. That would be the best of both worlds.
>

How then to distinguish between exception thrown out of constructor
and exception thrown from post-constructro function? Should the
object be deleted in second case?

/Pavel

Andrei Alexandrescu (See Website for Email)

unread,
Jul 10, 2004, 7:13:50 AM7/10/04
to
"Pavel Vozenilek" <pavel_v...@yahoo.co.uk> wrote in message
news:2l84slF...@uni-berlin.de...

>
> "Andrei Alexandrescu wrote:
>
> > Ideally C++ would allow a function to be called just after the
constructor
> > of the whole object completes. That would be the best of both worlds.
> >
> How then to distinguish between exception thrown out of constructor
> and exception thrown from post-constructro function? Should the
> object be deleted in second case?

The behavior ought to be exactly as if the post-constructor was called
*after* the constructor completed. It's just an automation of a function
call. So the object should be destroyed (and deleted if allocated with new)
if the exception occurs in the post-constructor.

Andrei

Andrei Alexandrescu (See Website for Email)

unread,
Jul 10, 2004, 7:15:26 AM7/10/04
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:40eeae66....@news.individual.net...

> * Andrei Alexandrescu (See Website for Email):
> > "Nicola Musatti" <Nicola....@ObjectWay.it> wrote in message
> > news:a327cf48.04070...@posting.google.com...
> > > Allowing virtual functions to act upon the non initialized portion of
> > > the most derived class instance? In my opinion this is one thing C++
> > > got right, in contrast with other languages (Delphi comes to mind)
> > > which forsake the base/derived class boundary in order to allow
> > > virtual constructors.
> >
> > Ideally C++ would allow a function to be called just after the
constructor
> > of the whole object completes. That would be the best of both worlds.
>
> C++ already allows that; it just doesn't do it for you.

That could be said about a lot of things :o).

> And it's not at all a good solution: it leads to degenerate code such as
> in MFC where all real construction is done in that post-constructor-call
> function. And that means that derived class code may and by Murphy's law
> _will_ be executed before even the base class' class invariant has been
> established, breaking type-safety (especially ironic that the standard
> library uses such technique and documents it, as I recall, "if the object
> is destroyed before it has been initialized ... the effect is undefined").

This is a confusion.

I was referring to an automatic insertion (by the compiler) of a member
function call just after the full object's construction completes. That, I
believe, is at the same time feasible, sound, and useful. (In particular it
would help solve a ton of the nasty problems related to constructors that
the try-block tried to solve and it couldn't.)

> I think the reason people want this horror is because they don't know or
> understand better, but you of all people should (posting in haste?).
> Techniques: FAQ item 23.4. It's not as if it's very difficult in any way.

Thanks for the implied compliment... I guess. :o) I wasn't posting in haste.


Andrei

Matthias Hofmann

unread,
Jul 10, 2004, 3:34:12 PM7/10/04
to
Alf P. Steinbach <al...@start.no> schrieb in im Newsbeitrag:
40eeae66....@news.individual.net...

[snip]

> established, breaking type-safety (especially ironic that the standard
> library uses such technique and documents it, as I recall, "if the object
> is destroyed before it has been initialized ... the effect is undefined").

Where does the standard library use that technique? And what if that
post-constructor function throws an exception - should the object be deleted
then?

Best regards,

Matthias

Francis Glassborow

unread,
Jul 10, 2004, 3:37:05 PM7/10/04
to
In article <2l84slF...@uni-berlin.de>, Pavel Vozenilek
<pavel_v...@yahoo.co.uk> writes

>
>"Andrei Alexandrescu wrote:
>
>> Ideally C++ would allow a function to be called just after the constructor
>> of the whole object completes. That would be the best of both worlds.
>>
>How then to distinguish between exception thrown out of constructor
>and exception thrown from post-constructro function? Should the
>object be deleted in second case?

So what we need is a new type of inheritance, more extreme even than
virtual bases (whose ctors run before anything else), one where the
'inherited' class ctor runs after everything else. Something like:

class x: finally y {
// whatever
};

No I am not proposing this, but it seems to be the only logical way to
tackle the things that are being asked for.

--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects

Alf P. Steinbach

unread,
Jul 10, 2004, 4:11:40 PM7/10/04
to
* Andrei Alexandrescu:
> * "Alf P. Steinbach":
>> * Andrei Alexandrescu:

>>>
>>> Ideally C++ would allow a function to be called just after the
>>> constructor of the whole object completes. That would be the best
>>> of both worlds.
>>
>> [It is] not at all a good solution: it leads to degenerate code such as

>> in MFC where all real construction is done in that post-constructor-call
>> function. And that means that derived class code may and by Murphy's law
>> _will_ be executed before even the base class' class invariant has been
>> established, breaking type-safety (especially ironic that the standard
>> library uses such technique and documents it, as I recall, "if the object
>> is destroyed before it has been initialized ... the effect is undefined").
>
> This is a confusion.

I agree that it is... ;-)


> I was referring to an automatic insertion (by the compiler) of a member
> function call just after the full object's construction completes.

Yes, that's what I assumed, if by "construction" you mean "constructor
execution".

Am I further correct in assuming that call would be _as if_ it
were placed manually after the constructor call (i.e. with dynamic
type == the type instantiated from), not inside the constructor body?

That leads to degenerate code such as in MFC where all real construction
is done in that post-constructor-call function. And it means that derived


class code may and by Murphy's law _will_ be executed before even the base
class' class invariant has been established, breaking type-safety
(especially ironic that the standard library uses such technique and
documents it, as I recall, "if the object is destroyed before it has been
initialized ... the effect is undefined").

> That, I believe, is at the same time feasible,

Yes.


> sound

Nope, see above.


> and useful. (In particular it
> would help solve a ton of the nasty problems related to constructors that
> the try-block tried to solve and it couldn't.)

I'm not sure what you're referring to here, but I see no problems that
aren't easily solved by conventional means, simply using sound design.

Re-mention: FAQ 23.4.

Example?


>
>> I think the reason people want this horror is because they don't know or
>> understand better, but you of all people should (posting in haste?).
>> Techniques: FAQ item 23.4. It's not as if it's very difficult in any way.
>
> Thanks for the implied compliment... I guess. :o)

You're welcome (and it was).

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Pavel Vozenilek

unread,
Jul 11, 2004, 6:24:44 AM7/11/04
to

"Andrei Alexandrescu wrote:

> I was referring to an automatic insertion (by the compiler) of a member

> function call just after the full object's construction completes. That, I
> believe, is at the same time feasible, sound, and useful.
>
...


> The behavior ought to be exactly as if the post-constructor was called
> *after* the constructor completed. It's just an automation of a function
> call.
>

Something similar (automatically called functions) was proposed
to standard (paper n1613 - support for Design By Contract,
written by Thorsten Ottosen).

Maybe post-constructor feature would fit into the proposal.

/Pavel

Matthias Hofmann

unread,
Jul 11, 2004, 6:28:03 AM7/11/04
to
Andrei Alexandrescu (See Website for Email)
<SeeWebsit...@moderncppdesign.com> schrieb in im Newsbeitrag:
2l9h7fF...@uni-berlin.de...

> "Pavel Vozenilek" <pavel_v...@yahoo.co.uk> wrote in message
> news:2l84slF...@uni-berlin.de...
> >
> > "Andrei Alexandrescu wrote:
> >
> > > Ideally C++ would allow a function to be called just after the
> constructor
> > > of the whole object completes. That would be the best of both worlds.
> > >
> > How then to distinguish between exception thrown out of constructor
> > and exception thrown from post-constructro function? Should the
> > object be deleted in second case?
>
> The behavior ought to be exactly as if the post-constructor was called
> *after* the constructor completed. It's just an automation of a function
> call. So the object should be destroyed (and deleted if allocated with
new)
> if the exception occurs in the post-constructor.

But this still leaves you with the problem of having to distinguish between
an exception thrown from the constructor (no deletion necessary) and an
exception thrown from the post-construction function (deletion necessary).
For example, you would be required to have the exception object carry a flag
indicating its origin, thus causing more trouble than your automatically
inserted function would solve.

Best regards,

Matthias

Andrei Alexandrescu (See Website for Email)

unread,
Jul 11, 2004, 6:28:39 AM7/11/04
to
"Francis Glassborow" <fra...@robinton.demon.co.uk> wrote in message
news:j6a3wtDo...@robinton.demon.co.uk...

> In article <2l84slF...@uni-berlin.de>, Pavel Vozenilek
> <pavel_v...@yahoo.co.uk> writes
> >
> >"Andrei Alexandrescu wrote:
> >
> >> Ideally C++ would allow a function to be called just after the
constructor
> >> of the whole object completes. That would be the best of both worlds.
> >>
> >How then to distinguish between exception thrown out of constructor
> >and exception thrown from post-constructro function? Should the
> >object be deleted in second case?
>
> So what we need is a new type of inheritance, more extreme even than
> virtual bases (whose ctors run before anything else), one where the
> 'inherited' class ctor runs after everything else. Something like:
>
> class x: finally y {
> // whatever
> };
>
> No I am not proposing this, but it seems to be the only logical way to
> tackle the things that are being asked for.

But that's not a kind of inheritance. The postconstructor call should be
decided by the base class, not by the code that derives from it.

A possible syntax would be:

struct Base {
void Post();
Base(some_arguments) -> Post();
Base(some_other_arguments) -> Post()
: member(expr), member(expr)
{ ctor body }
...
};

This allows the constructor of a base class to specify a postconstructor
call, and to specify different postconstructors and/or arguments depending
on the constructor.

Also, Post need not be virtual. When a full object is being constructed, the
compiler has full access to the exact type of the object. However, the "->
Fun" syntax must be present in the header (can't be modularized away).

Again, I have recently realized that such a feature would help a lot solve
about a bazillion really nasty problems related to constructors and
exceptions.


Andrei

Alf P. Steinbach

unread,
Jul 11, 2004, 6:29:05 AM7/11/04
to
* Matthias Hofmann:

> Alf P. Steinbach <al...@start.no> schrieb in im Newsbeitrag:
> 40eeae66....@news.individual.net...
>
> [snip]
>
> > established, breaking type-safety (especially ironic that the standard
> > library uses such technique and documents it, as I recall, "if the object
> > is destroyed before it has been initialized ... the effect is undefined").
>
> Where does the standard library use that technique?

std::basic_ios, §27.4.4.1/2.

It's only partially what Andrei talks about, though; it's not a virtual
function, and I don't think it calls any virtual function.

Which makes it even harder to figure out the non-rationale of the
non-design.


> And what if that
> post-constructor function throws an exception - should the object be deleted
> then?

In that case the behavior is undefined. Period. Heh... :-)

Presumably it's so simple it can't fail, but it doesn't have an exception
specification.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Andrei Alexandrescu (See Website for Email)

unread,
Jul 11, 2004, 6:30:36 AM7/11/04
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:40efdd2b...@news.individual.net...

> > I was referring to an automatic insertion (by the compiler) of a member
> > function call just after the full object's construction completes.
>
> Yes, that's what I assumed, if by "construction" you mean "constructor
> execution".

This continues to be a confusion. :oD

The confusion seem to stem from neglecting the word "full" in my sentence.
The call would be inserted after the /full/ object has been constructed.
Example (assuming some random post-ctor syntax):

struct Base {
Base() post = Post() { cout << "base ctor body\n"; }
void Post() { cout << "blah\n" };
};

struct Derived {
Derived() { cout << "derived ctor body\n"; }
};

Now the sound semantics I was referring to is:

Derived object;

would print:

base ctor body
derived ctor body
blah

(Of course, Derived can override Post to do something else.) The point of
the example is to show that Post() is called after the *full* object has
completed initialization, so it is an initialized, valid object.

> Am I further correct in assuming that call would be _as if_ it
> were placed manually after the constructor call (i.e. with dynamic
> type == the type instantiated from), not inside the constructor body?

I am not sure, because you don't specify which constructor call you are
referring to.

> That leads to degenerate code such as in MFC where all real construction
> is done in that post-constructor-call function. And it means that derived
> class code may and by Murphy's law _will_ be executed before even the base
> class' class invariant has been established,

I'm not sure how this could ever happen. The base class constructor
establishes the base class' invariant. The derived class has absolutely no
crack at doing anything until the base class' constructor has long finished
execution.

> Re-mention: FAQ 23.4.

I had read that faq. It's a compendium of the old well known techniques, all
of which I believe the posters in this thread are familiar with, and all of
which shed no new light on the issue. So why rementioning?


Andrei

Dave Harris

unread,
Jul 11, 2004, 6:31:54 AM7/11/04
to
SeeWebsit...@moderncppdesign.com (Andrei Alexandrescu \(See Website
for Email\)) wrote (abridged):

> Ideally C++ would allow a function to be called just after the
> constructor of the whole object completes. That would be the best
> of both worlds.

What happens when it is overridden? Who is responsible for calling the
base class version, and in what order?

As things stand, you have a class invariant and one function which is
called before the class invariant is true. That function is the
constructor and it's job is exactly to set up the class invariant. If we
add a post-constructor initialiser, it seems to me we effectively have 2
class invariants, one set up by the constructor, and one set up by the
post-constructor initialiser. The same problems that arise with Java
constructors now arise with the post-constructor initialiser. An
essentially clean and simple system would become a complex and muddled
one, with lots of order dependencies.

-- Dave Harris, Nottingham, UK

Scott Meyers

unread,
Jul 11, 2004, 7:39:45 PM7/11/04
to
On 10 Jul 2004 16:11:40 -0400, Alf P. Steinbach wrote:
> Re-mention: FAQ 23.4.

I looked this up before and dismissed it, but since you've rementioned it, I
just looked it up again. 23.4 is "What does it mean that the 'virtual table'
is an unresolved external?" How does this have *anything* to do with what
this thread is about?

Scott

Francis Glassborow

unread,
Jul 11, 2004, 7:47:18 PM7/11/04
to
In article <2lavqhF...@uni-berlin.de>, "Andrei Alexandrescu (See
Website for Email)" <SeeWebsit...@moderncppdesign.com> writes

> > So what we need is a new type of inheritance, more extreme even than
> > virtual bases (whose ctors run before anything else), one where the
> > 'inherited' class ctor runs after everything else. Something like:
> >
> > class x: finally y {
> > // whatever
> > };
> >
> > No I am not proposing this, but it seems to be the only logical way to
> > tackle the things that are being asked for.
>
>But that's not a kind of inheritance. The postconstructor call should be
>decided by the base class, not by the code that derives from it.

A virtual base class imposes a requirement on every class that derives
from the class with a virtual base. That requirement is that the virtual
base ctor be run first. My suggestion is that we have a mechanism that
forces a ctor body be run after everything else. And if there were
several then make the ordering rule right-to-left depth last (i.e. the
exact reverse of normal ordering for ctors.

Now the syntax for such a beast would need careful consideration. See
below for some ideas.


>
>A possible syntax would be:
>
>struct Base {
> void Post();
> Base(some_arguments) -> Post();
> Base(some_other_arguments) -> Post()
> : member(expr), member(expr)
> { ctor body }
> ...
>};
>
>This allows the constructor of a base class to specify a postconstructor
>call, and to specify different postconstructors and/or arguments depending
>on the constructor.
>
>Also, Post need not be virtual. When a full object is being constructed, the
>compiler has full access to the exact type of the object. However, the "->
>Fun" syntax must be present in the header (can't be modularized away).

But the trouble is that it results in quite a lot of problem when it
comes to trying to specify both the syntax and the semantics. How would
this interact with the currently proposed forwarding ctors? And that is
just for starters.

The property of 'post construction' would need to be consistently
applied to all ctors including any compiler generated copy ctors.

>
>Again, I have recently realized that such a feature would help a lot solve
>about a bazillion really nasty problems related to constructors and
>exceptions.

Now my preliminary thoughts on this is that we introduce a new, heavily
constrained class type (actually as close to an interface as we can get
in C++). It has no instance data, it has static members + pure virtual
functions. It has a default (but not compiler generated) ctor and
possibly copy ctor the bodies of which call the provided pure virtuals.

Given such an entity + a mechanism for delaying its ctor till after the
completion of the ctor for the most derived class I think we could
achieve most of what people want by way of a post constructor.

I think that such a mechanism, among other things, could be used to
check that a pure virtual function's final overrider was provided by the
most derived class.

--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects

Alf P. Steinbach

unread,
Jul 11, 2004, 7:51:10 PM7/11/04
to
* Andrei Alexandrescu (See Website for Email):
> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:40efdd2b...@news.individual.net...
> > > I was referring to an automatic insertion (by the compiler) of a member
> > > function call just after the full object's construction completes.
> >
> > Yes, that's what I assumed, if by "construction" you mean "constructor
> > execution".
>
> This continues to be a confusion. :oD

Yes, I agree... ;-)


> The confusion seem to stem from neglecting the word "full" in my sentence.

No.


> The call would be inserted after the /full/ object has been constructed.

Yes, that's what I assumed.


> > Am I further correct in assuming that call would be _as if_ it
> > were placed manually after the constructor call (i.e. with dynamic
> > type == the type instantiated from), not inside the constructor body?
>
> I am not sure, because you don't specify which constructor call you are
> referring to.

Well I mean the same as you, see above.

Isn't "dynamic type == the type instantiated from" clear enough?

Mumble mumble mumble.

> > That leads to degenerate code such as in MFC where all real construction
> > is done in that post-constructor-call function. And it means that derived
> > class code may and by Murphy's law _will_ be executed before even the base
> > class' class invariant has been established,
>
> I'm not sure how this could ever happen. The base class constructor
> establishes the base class' invariant.

That's what in practice _seldom_ or _never_ happens when you use a
post-constructor-call facility.

For example (this is my favorite example) in concrete class Window
post construction may assign a native window handle of correct kind.

And most if not all of the construction in Window, and in derived
classes such as Button, depends on having that handle, so without
other techniques being used (which would render post-constuction-call
totally unnecessary) all real construction ends up in the post.


> The derived class has absolutely no
> crack at doing anything until the base class' constructor has long finished
> execution.

That's correct. But the inference that this means a fully established
class invariant is not correct. For much of the point of a
post-constructor-call is to _defer_ some or all of the real construction
to a point where the dynamic type == the type instantiated from.


> > Re-mention: FAQ 23.4.
>
> I had read that faq. It's a compendium of the old well known techniques, all
> of which I believe the posters in this thread are familiar with, and all of
> which shed no new light on the issue. So why rementioning?

Because this debate is here in spite of the (elementary) techniques
presumably being well-known.


[re question apparently forgotten]

Do you have an example of that exception problem you mentioned, that
you thought an automatic post-constructor-call could in some way help
solving?

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

David Abrahams

unread,
Jul 11, 2004, 7:53:41 PM7/11/04
to
"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsit...@moderncppdesign.com> writes:

> Again, I have recently realized that such a feature would help a lot solve
> about a bazillion really nasty problems related to constructors and
> exceptions.

I would love to see some examples, since in my experience, generally
speaking, problems with ctors and exceptions are rare. I think I know
some really nasty hacks that will allow you to get what you want,
though.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

Dave Harris

unread,
Jul 11, 2004, 7:56:21 PM7/11/04
to
hof...@anvil-soft.com (Matthias Hofmann) wrote (abridged):

> But this still leaves you with the problem of having to distinguish
> between an exception thrown from the constructor (no deletion
> necessary) and an exception thrown from the post-construction
> function (deletion necessary).

Deletion is probably necessary in both cases. From a pragmatic point of
view there are probably no extent references to the new object, so if it
is not deleted automatically we have a memory leak.

Also, I think it could be argued that the class now has two class
invariants, one set up by the constructor and one by the post-constructor.
If the post-constructor throws, it has failed to set the second class
invariant and so the object should be deleted for that reason, too.

Indeed, we probably need a pre-destructor as well. Suppose we want to
enforce a rule that "every object is registered in a global database for
the duration of its lifetime", where registration needs fully constructed
objects. The post-constructor registers its object, and if it does not
throw the destructor needs to unregister it. If the post-constructor does
throw, then the object has probably not been registered, and the
destructor does not need to do anything. So arguably the unregistering
needs to be moved to a pre-destructor which is invoked if and only if the
post-constructor was invoked and did not throw. Symmetry.

Meanwhile the Java problem exists: member functions cannot rely on only
being called for registered objects. We can't actually enforce the rule
very reliably.

Personally I think this is all madness. But if we do go this route, I hope
we look at a full method combinator system of the sort seen in Common
Lisp. In other words, pre-, post- and around- options on all methods, not
just constructors (and destructors).

-- Dave Harris, Nottingham, UK

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Jul 11, 2004, 8:04:42 PM7/11/04
to
* Scott Meyers:

> On 10 Jul 2004 16:11:40 -0400, Alf P. Steinbach wrote:
> > Re-mention: FAQ 23.4.
>
> I looked this up before and dismissed it, but since you've rementioned it, I
> just looked it up again. 23.4 is "What does it mean that the 'virtual table'
> is an unresolved external?" How does this have *anything* to do with what
> this thread is about?

You're probably using a "stale" copy of the FAQ (or perhaps the book?).

See <url:
http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.4>.

Btw., if you have any good idea on how to avoid the "stale" copy problem I
know that Marshall Cline (the FAQ maintainer) would be very interested to
hear about it; this problem is one of the reasons why there's currently no
longer a "get the FAQ zip by e-mail" option as there used to be, and which
this FAQ was renowned for.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Andreas Gieriet

unread,
Jul 11, 2004, 8:09:36 PM7/11/04
to
A suggestion for a language extension that might help solving this issue
to a certain extent:

Static Virtual Methods
======================

Allow a method to be "virtual static", that is, it is in the vtable but
has no "this" pointer. From a user point of view, this would allow to
have virtual methods that do not depend on the object.
Such a method could be called in a base class constructor independent of
the state of the construction.

Of course, the current approach of most of the compilers to modify the
vptr in the base class would not work anymore.
E.g., the compiler would need access to the vptr of both classes - the
one from the base class (for the plain virtual functions) and the one of
the derived class (for the static virtual functions) - which looks
feasible to me.

Example:
----8<----8<----8<----8<----8<----8<----8<----8<----
class Base {
public:
Base() { doLog(getId()); }
virtual ~Base() {}
private:
virtual static const char* getId() = 0; // <-- no "this"
void doLog(const char* id) { std::cout << "ID=" << id << std::endl; }
};

class Derived : public Base {
public:
Derived() : Base() {}
private:
const char* getId() { return "Derived"; } // <-- no "this"
};
----8<----8<----8<----8<----8<----8<----8<----8<----

The compiler could even allow to call any (virtual) static method in a
virtual static method.

Is such a construct already considered for C++0x?

Cheers

Andi

--
Andreas Gieriet mailto:andreas...@externsoft.ch
eXternSoft GmbH http://www.externsoft.com/
engineering & consulting phone:++41 79 751 7650

Andrei Alexandrescu (See Website for Email)

unread,
Jul 11, 2004, 8:10:16 PM7/11/04
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:40f05204...@news.individual.net...
> * Matthias Hofmann:

> > And what if that
> > post-constructor function throws an exception - should the object be
deleted
> > then?
>
> In that case the behavior is undefined. Period. Heh... :-)
>
> Presumably it's so simple it can't fail, but it doesn't have an exception
> specification.

As I said, the behavior can be made defined and sound without difficulty.
There is no complication that post-construction adds as far as exceptions
are concerned. I gave an example in another post in this thread.

Andrei

Andrei Alexandrescu (See Website for Email)

unread,
Jul 11, 2004, 8:10:53 PM7/11/04
to
"Dave Harris" <bran...@cix.co.uk> wrote in message
news:memo.20040710233738.132A@brangdon.m...

> What happens when it is overridden? Who is responsible for calling the
> base class version, and in what order?

There is no responsibility. That postconstructor is there not to establish
invariants, but to give derived classes a crack at performing operations on
the fully initialized object. There are many needs for that, such as
registering objects with factories, the Observer pattern.

> As things stand, you have a class invariant and one function which is
> called before the class invariant is true. That function is the
> constructor and it's job is exactly to set up the class invariant. If we
> add a post-constructor initialiser, it seems to me we effectively have 2
> class invariants, one set up by the constructor, and one set up by the
> post-constructor initialiser.

I understand your concern. But the same argument could be made by a
procedural design aficionado against virtual functions: "If someone
overrides a virtual function, who is responsible for calling the base class
version, and in what order?" etc.

The point of the post-constructor is not to establish invariants, but to do
what virtuals are best at: offer customization capabilites. For example,
I've had such a need in the Observer pattern implementation:

* The Subject class takes an Observer as an argument. It will automatically
subscribe to the Observer.

* The Subject, whenever an Obsever subscribes, will send its state as a
burst of events.

* The events are meant for the most derived class, not for the base class.
The base class doesn't know what to do with the events.

> The same problems that arise with Java
> constructors now arise with the post-constructor initialiser. An
> essentially clean and simple system would become a complex and muddled
> one, with lots of order dependencies.

No. Again, post-constructors are the best of both worlds. In Java they can
catch the object in a partially initialized state. (This, by the way, makes
const very hard to implement in Java.)


Andrei

Andrei Alexandrescu (See Website for Email)

unread,
Jul 11, 2004, 8:15:10 PM7/11/04
to
"Matthias Hofmann" <hof...@anvil-soft.com> wrote in message
news:ccpejk$apf$1...@news1.nefonline.de...

> But this still leaves you with the problem of having to distinguish
between
> an exception thrown from the constructor (no deletion necessary) and an
> exception thrown from the post-construction function (deletion necessary).

You must mean "destruction" instead of deletion.

> For example, you would be required to have the exception object carry a
flag
> indicating its origin, thus causing more trouble than your automatically
> inserted function would solve.

No need. This is because the entire code is generated at the construction
site:

struct C {
C() -> Post() { ctor body }
void Post() { post body }
};

Now consider an object of type C is created:

C obj;

Then the compiler rewrites that into the following "C++ without postctors"
code:

char __buf[sizeof(C)]; // assume it's also aligned
C& obj = __invoke_ctor(C); // if it throws, everything goes its merry way
try {
obj.Post();
}
catch (...) {
obj.C::~C();
throw;
}

The code is similar in the case of "C* pObj = new C", with the added calls
to the allocation and deallocation functions:

char* __buf = C::operator new(sizeof C);
C* pObj;
try {
pObj = &__invoke_ctor(C);
}
catch (...) {
C::operator delete(__buf, sizeof(C));
throw;
}
try {
pObj.Post();
}
catch (...) {
pObj->C::~C();
C::operator delete(pObj, sizeof(C));
throw;
}

So the construction site always has enough information to construct and
post-construct the object correctly.


Andrei

Alf P. Steinbach

unread,
Jul 12, 2004, 7:15:24 AM7/12/04
to
* Andrei Alexandrescu:
> * "Alf P. Steinbach":
> > * Matthias Hofmann:
> > > And what if that post-constructor function throws an
> > > exception - should the object be deleted then?
> >
> > In that case the behavior is undefined. Period. Heh... :-)
> >
> > Presumably it's so simple it can't fail, but it doesn't have an exception
> > specification.
>
> As I said, the behavior can be made defined and sound without difficulty.

Uhm, you snipped quite a lot of context here.

The "undefined" is for the specific example in the standard library, and
it's specified as undefined by the standard (see snipped text for ref).


> There is no complication that post-construction adds as far as exceptions
> are concerned. I gave an example in another post in this thread.

I was unable to find any example that seemed relevant.

However, there is also an exception problem with the post-constructor-call
idea in general. It is a classical dilemma thing; you can choose one horn
or the other, but whichever is chosen it will cause some unpleasantness.
Of course the classical solution is to go between the horns, but I don't
see how (perhaps you have already considered this and found such a way?).

First, and I know this isn't what you've described but it's the Left Horn
(TM), if an exception from the post() function doesn't automatically cause
object destruction, as with an exception from a constructor, then we have
the case of a potential zombie object, a "vesen" that kills on contact.

Second, the Right Horn (TM), if an exception from the post() function does
cause automatic object destruction, then when implementing a Derived class
you can never know whether Base is fully constructed or not (no exception
will come sneaking up on you) when the Base constructor has executed
successfully. And that means that if Derived construction involves some
costly (time, memory, other) resource acquisition it better be done in a
post() function in Derived. And that means, irrespective of whether one
wants or otherwise would "need" to use the post() facility, that all
costly construction is in practice moved to post() functions. And that in
turn means that class invariants will in general not be fully established
by constructors. And that means extra state-checking everywhere and hence
more complex & less maintanable code and more bug opportunities.

Now I've argued elsewhere in this thread that a post-construction-call
facility would likely be used for such things as acquiring
derived-class-specific resources such as native window handles, and that
is another force that also moves real construction to post() functions.

So in essence the Right Horn (TM) is a more or less complete dismantling
of the type safety currently built into the C++ constructor mechanism
(and it's not a theoretical horn, it's the experience from e.g. MFC).


I see only disadvantages with the approach, and no advantages except
conceptual simplicity when disregarding the details; what is a concrete
example that would not be easily implemented by conventional techniques?

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Andrei Alexandrescu (See Website for Email)

unread,
Jul 12, 2004, 7:19:35 AM7/12/04
to
"David Abrahams" <da...@boost-consulting.com> wrote in message
news:uoemmu...@boost-consulting.com...

> "Andrei Alexandrescu \(See Website for Email\)"
<SeeWebsit...@moderncppdesign.com> writes:
>
> > Again, I have recently realized that such a feature would help a lot
solve
> > about a bazillion really nasty problems related to constructors and
> > exceptions.
>
> I would love to see some examples, since in my experience, generally
> speaking, problems with ctors and exceptions are rare. I think I know
> some really nasty hacks that will allow you to get what you want,
> though.

The innumerable and never-ending smart_ptr construction problems that you,
Dave Held, myself, and others have discussed, and that Dave Held and I wrote
in CUJ about, are a good start...

Also, when I was playing with implementing std::vector, I noticed that
implementing the constructors is harder than it should, and could, be.


Andrei

Andrei Alexandrescu (See Website for Email)

unread,
Jul 12, 2004, 7:19:57 AM7/12/04
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:40f11e87....@news.individual.net...

> > I'm not sure how this could ever happen. The base class constructor
> > establishes the base class' invariant.
>
> That's what in practice _seldom_ or _never_ happens when you use a
> post-constructor-call facility.

Now the thing is, this all might be a terminology issue. When I say that
post-constructors (as I suggest could be defined) are "sound", I mean
"soundness" in a very precise way. You said flat out that soundness is
absent. Now I see that you really meant something like "it could be abused"
which is something very different than that precise meaning I was referring
to.

> [re question apparently forgotten]
>
> Do you have an example of that exception problem you mentioned, that
> you thought an automatic post-constructor-call could in some way help
> solving?

I've shown an example in another post in the same thread.


Andrei

Andrei Alexandrescu (See Website for Email)

unread,
Jul 12, 2004, 7:22:51 AM7/12/04
to
"Francis Glassborow" <fra...@robinton.demon.co.uk> wrote in message
news:rzblipRy...@robinton.demon.co.uk...

> >But that's not a kind of inheritance. The postconstructor call should be
> >decided by the base class, not by the code that derives from it.
>
> A virtual base class imposes a requirement on every class that derives
> from the class with a virtual base. That requirement is that the virtual
> base ctor be run first. My suggestion is that we have a mechanism that
> forces a ctor body be run after everything else. And if there were
> several then make the ordering rule right-to-left depth last (i.e. the
> exact reverse of normal ordering for ctors.

All that doesn't contradict what I said, nor backs up the idea that the
postconstructor mechanism is decided by the subclass. Again, the base class
should be the one that establishes the protocol - and the others can choose
to override it in whatever ways.

> But the trouble is that it results in quite a lot of problem when it
> comes to trying to specify both the syntax and the semantics. How would
> this interact with the currently proposed forwarding ctors? And that is
> just for starters.

I knew you were going to ask this, Francis. :o)

> Now my preliminary thoughts on this is that we introduce a new, heavily
> constrained class type (actually as close to an interface as we can get
> in C++). It has no instance data, it has static members + pure virtual
> functions. It has a default (but not compiler generated) ctor and
> possibly copy ctor the bodies of which call the provided pure virtuals.
>
> Given such an entity + a mechanism for delaying its ctor till after the
> completion of the ctor for the most derived class I think we could
> achieve most of what people want by way of a post constructor.
>
> I think that such a mechanism, among other things, could be used to
> check that a pure virtual function's final overrider was provided by the
> most derived class.

These all sounds like interesting ideas.


Andrei

Scott Meyers

unread,
Jul 12, 2004, 7:24:19 AM7/12/04
to
On 11 Jul 2004 20:04:42 -0400, Alf P. Steinbach wrote:
> You're probably using a "stale" copy of the FAQ (or perhaps the book?).

Silly me, I just clicked on the first hit after googling "C++ FAQ 23.4",
which turns out to be a very old copy of the FAQ in Finland. The next hits
are old versions of the FAQ in the UK, the Czech Republic, Chile, and
Poland. Only then does the version at parashift.com come up. It really
is a web that's world wide.

I encourage others to beware, lest they fall into the trap that I did.

I'd like to chastize Marshall for renumbering his FAQs (is it really
helpful that what used to be 23.4 is now 23.7?), but as I'm currently in
the process of performing significant renumbering of the Items in Effective
C++ for a third edition, I'll refrain :-)

Scott

Francis Glassborow

unread,
Jul 12, 2004, 5:29:01 PM7/12/04
to
In article <MPG.1b5bcb516...@news.hevanet.com>, Scott Meyers
<Use...@aristeia.com> writes

>I'd like to chastize Marshall for renumbering his FAQs (is it really
>helpful that what used to be 23.4 is now 23.7?), but as I'm currently in
>the process of performing significant renumbering of the Items in Effective
>C++ for a third edition, I'll refrain :-)

If you intend renumbering in the third edition, please ensure that it
comes with an easy to use mapping of new numbers to old and vice-versa
(as we are only dealing with a list of 50 items one list should
suffice). I think this is important because people often refer to your
books and we should avoid confusion that might result from different
editions having substantially different numbering.


--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects

David Abrahams

unread,
Jul 12, 2004, 5:36:54 PM7/12/04
to
"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsit...@moderncppdesign.com> writes:

> "David Abrahams" <da...@boost-consulting.com> wrote in message
> news:uoemmu...@boost-consulting.com...
> > "Andrei Alexandrescu \(See Website for Email\)"
> <SeeWebsit...@moderncppdesign.com> writes:
> >
> > > Again, I have recently realized that such a feature would help
> > >a lot solve about a bazillion really nasty problems related to
> > >constructors and exceptions.
> >
> > I would love to see some examples, since in my experience,
> > generally speaking, problems with ctors and exceptions are rare.
> > I think I know some really nasty hacks that will allow you to get
> > what you want, though.

Actually I don't :(

> The innumerable and never-ending smart_ptr construction problems
> that you, Dave Held, myself, and others have discussed, and that
> Dave Held and I wrote in CUJ about, are a good start...

But that is a *highly* specialized case dealing with a
newly-constructed object that takes ownership of a resource (normally
that job should be encapsulated in smart pointers ;->) and policy
roles that, while mostly orthogonal, contain some overlap. I've never
seen a design where those kinds of issues were as knotty. My sense
was that at least part of the issue was that you were wedded to an
idea of the original design's "conceptual purity", when that design
didn't fully account for exception-safety issues. It appeared to me
that you ended up fighting against the design of C++. The language
has a "conceptual purity" of its own, and sometimes it makes sense to
let that tell you how to organize your own designs. Sometimes the
language is even right ;->.

> Also, when I was playing with implementing std::vector, I noticed that
> implementing the constructors is harder than it should, and could, be.

The answer for std::vector is the same as anywhere else: partition the
destruction semantics into a common base class or data member. The
existing language mechanisms deal with that case cleanly and
elegantly.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Gerhard Menzl

unread,
Jul 12, 2004, 5:37:59 PM7/12/04
to
Dave Harris wrote:

> Indeed, we probably need a pre-destructor as well. Suppose we want to
> enforce a rule that "every object is registered in a global database for
> the duration of its lifetime", where registration needs fully constructed
> objects. The post-constructor registers its object, and if it does not
> throw the destructor needs to unregister it. If the post-constructor does
> throw, then the object has probably not been registered, and the
> destructor does not need to do anything. So arguably the unregistering
> needs to be moved to a pre-destructor which is invoked if and only if the
> post-constructor was invoked and did not throw. Symmetry.

A few years ago I found myself in a situation where I would have given
my right ... er ... pinkie's fingernail for a
post-constructor/pre-destructor facility. The problem involved the
behaviour of the Observer pattern in multithreaded code.

The full discussion is available here:

http://tinyurl.com/4zvpz

--
Gerhard Menzl

Humans may reply by replacing the obviously faked part of my e-mail
address with "kapsch".

Alf P. Steinbach

unread,
Jul 12, 2004, 5:39:30 PM7/12/04
to
* Andrei Alexandrescu (See Website for Email):
> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:40f11e87....@news.individual.net...
> > > I'm not sure how this could ever happen. The base class constructor
> > > establishes the base class' invariant.
> >
> > That's what in practice _seldom_ or _never_ happens when you use a
> > post-constructor-call facility.
>
> Now the thing is, this all might be a terminology issue.

Nope, not in the main... ;-)

Consider that perhaps all participants here do understand what you mean by
the syntax -> in an initializer list, and have always understood that.

Then consider the arguments presented in that light.


> When I say that
> post-constructors (as I suggest could be defined) are "sound", I mean
> "soundness" in a very precise way. You said flat out that soundness is
> absent. Now I see that you really meant something like "it could be abused"
> which is something very different than that precise meaning I was referring
> to.

On the contrary, the meaning "it could be used in a type-safe manner" (by
very disciplined programmers) is the imprecise meaning.

By that measure assembly language is sound.

If such soundness, whatever it is, is an argument in favor of post-
constructor-calls then the only alternative must be something even worse
than assembly language, and you have yet to provide an example.


> > [re question apparently forgotten]
> >
> > Do you have an example of that exception problem you mentioned, that
> > you thought an automatic post-constructor-call could in some way help
> > solving?
>
> I've shown an example in another post in the same thread.

I'm sure it most be one snippet or other that I have dismissed as
irrelevant, unable to see the relevance, so, could you please repeat it
(not just an article id) and explain clearly (A) what this exception
problem is, in your opinion, (B) how a post-constructor-call provides a
simple solution, and (C) why conventional techniques, such as passing data
or code up the base constructor chain, do not?

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Andrei Alexandrescu (See Website for Email)

unread,
Jul 12, 2004, 5:54:46 PM7/12/04
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:40f1d777...@news.individual.net...

> * Andrei Alexandrescu:
> > * "Alf P. Steinbach":
> > > * Matthias Hofmann:
> > > > And what if that post-constructor function throws an
> > > > exception - should the object be deleted then?
> > >
> > > In that case the behavior is undefined. Period. Heh... :-)
> > >
> > > Presumably it's so simple it can't fail, but it doesn't have an
exception
> > > specification.
> >
> > As I said, the behavior can be made defined and sound without
difficulty.
>
> Uhm, you snipped quite a lot of context here.
>
> The "undefined" is for the specific example in the standard library, and
> it's specified as undefined by the standard (see snipped text for ref).

Ok.

> > There is no complication that post-construction adds as far as
exceptions
> > are concerned. I gave an example in another post in this thread.
>
> I was unable to find any example that seemed relevant.

I have the feeling that I'll see "But that's totally irrelevant!" after
pasting what I was referring to, but I'll do it anyway. Again, what I am
referring to is that post-constructors can be implemented such that the
behavior in the presence of exceptions is well-defined.

C obj;

> However, there is also an exception problem with the post-constructor-call


> idea in general. It is a classical dilemma thing; you can choose one horn
> or the other, but whichever is chosen it will cause some unpleasantness.
> Of course the classical solution is to go between the horns, but I don't
> see how (perhaps you have already considered this and found such a way?).
>
> First, and I know this isn't what you've described but it's the Left Horn
> (TM), if an exception from the post() function doesn't automatically cause
> object destruction, as with an exception from a constructor, then we have
> the case of a potential zombie object, a "vesen" that kills on contact.

Indeed I haven't described it and it is not worth discussing anymore. The
"post" in post-constructor makes it quite clear...

> Second, the Right Horn (TM), if an exception from the post() function does
> cause automatic object destruction, then when implementing a Derived class
> you can never know whether Base is fully constructed or not

Wrong.

The Base has been constructed by its constructor. The constructor does
construction. The post-constructor offers customizable behavior /after/
construction.

> (no exception
> will come sneaking up on you) when the Base constructor has executed
> successfully.

The Base constructor /has/ executed successfully by the time the
post-constructor is executing.

> And that means that if Derived construction involves some
> costly (time, memory, other) resource acquisition it better be done in a
> post() function in Derived.

I'm not sure where this inference comes from. I believe it comes from the
wrong antecedents.

> And that means, irrespective of whether one
> wants or otherwise would "need" to use the post() facility, that all
> costly construction is in practice moved to post() functions. And that in
> turn means that class invariants will in general not be fully established
> by constructors. And that means extra state-checking everywhere and hence
> more complex & less maintanable code and more bug opportunities.

This, I believe, also follows from the wrong hypotheses.

> Now I've argued elsewhere in this thread that a post-construction-call
> facility would likely be used for such things as acquiring
> derived-class-specific resources such as native window handles, and that
> is another force that also moves real construction to post() functions.
>
> So in essence the Right Horn (TM) is a more or less complete dismantling
> of the type safety currently built into the C++ constructor mechanism
> (and it's not a theoretical horn, it's the experience from e.g. MFC).

Wrong.

The constructor constructs objects as it always did like a champ. The
postconstructor is just like any virtual function - it can be overridden by
derived classes. So nobody would do construction in the *post*-constructor.
By definition of the term. :o)

I've worked with MFC before, and I understand their motivation for not
always creating or acquiring the native window in the constructor. That has
more to do with the way the OS creates and manipulates windows, than with
postconstructors being "evil".

It is good to not use terms with established specific meaning such as "type
safety" to refer to other things.

> I see only disadvantages with the approach, and no advantages except
> conceptual simplicity when disregarding the details; what is a concrete
> example that would not be easily implemented by conventional techniques?

The Observer pattern.


Andrei

Robert Kindred

unread,
Jul 12, 2004, 5:56:59 PM7/12/04
to

"Gerhard Menzl" <gerhar...@spambucket.net> wrote in message
news:40ee4373$1...@news.kapsch.co.at...
> Robert Kindred wrote:
>
<snip>
>
> How so? Constructors that fail throw and kill the object in mid-birth.
> No zombie state, no memory hogging. It is secondary initialization and
> deinitialization functions like open() and close() that cause half-baked
> objects with funny extra states. Unless the problem domain explicitly
> calls for it, such design is best avoided.

I am afraid I am at a loss for some references, and I will pursue this
further. I know that the object can be killed in mid-birth, as you put, but
I cannot see how the memory can be reclaimed.

Robert Kindred

>
<snip>


>
> --
> Gerhard Menzl
>
> Humans may reply by replacing the obviously faked part of my e-mail
> address with "kapsch".

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Shannon Barber

unread,
Jul 12, 2004, 6:05:49 PM7/12/04
to
Francis Glassborow <fra...@robinton.demon.co.uk> wrote in message news:<BV3PrGEc...@robinton.demon.co.uk>...
> In article <de001473.04070...@posting.google.com>, Shannon
> Barber <shannon...@myrealbox.com> writes
> >I think that's a fundamental C++ problem; shouldn't the vtbl be
> >completely constructed prior to the execution of any ctor code? It
> >would make object construction a two-stage process (only for
> >'virtualized' classes), but I think it would be worth it. Does
> >someone with more clout than me want to submit it for C++0x?
>
> Looks simple doesn't it? Actually it is very far from being so. By the
> time we have multiple inheritance and virtual base classes in the mix it
> becomes a nightmare to even understand all the alternatives and their
> implications.

I think it already is a nightmare to understand :o)

> However putting that to one side, the problem actually has little to do
> with any putative vtbl but has to do with the initialisation of member
> data. What we have today guarantees that any well-designed polymorphic
> concrete class will behave correctly as a base class. What you propose
> breaks that guarantee:
>
> class base {
> int i;
> public:
> base(): i(0) {printon(clog);}
> virtual void printon(ostream & out){out << i <<'\n';}
> };
>
> class derived: public base {
> std::string message;
> public:
> derived():message("Help!"){};
> void printon(ostream & out){out << i <<", " << message << '\n';}
> };
>
> Under current rules those definitions are (at least in the context of
> what we are discussing) OK. Change the rule to the one you are
> advocating and we have UB.
>
> In simple terms, even if we wanted to, we could not contemplate the such
> a change.


<attempt to remove egg from face>
What I really meant was /all/ ctor inititalization should be performed
prior to /any/ ctor code being executed, not just the vtable.

With your example,
Phase 1:
Base::i is initialized to 0
Derived::message is initialized to "Help!"

Phase 2:
Base::ctor is executed
Derived::printon(clog) is executed
Derived::ctor is executed


It would break if you "initialized" message as so:
Derived::Derived()
{
message = "I need Help!";
}

But you shouldn't be doing that ;)


We already have special syntax for the initialization, it's screaming
at us to perform it first.

Shannon Barber

unread,
Jul 12, 2004, 6:06:29 PM7/12/04
to
Antoun Kanawati <ant...@comcast.net> wrote in message news:<Y63Hc.26747$JR4.14969@attbi_s54>...

<snip>

>
> I hope you're not suggesting that we hook up the final vtable,
> and leave the rest of the object as a random collection of bits.

I do want to hook-up to the final vtable, but after member
initialization code has executed. See response to Francis Glassborow.


> Even with a sink-or-swim frame of mind, this sort of stuff is too
> evil.


>
> > Does someone with more clout than me want to submit it for C++0x?
>

> Hey, at least offer this person a free beer, or a dinner. Approaching
> clout has certain protocols, you know.

Hum, approaching clout sounds much like a date :o)

Andrei Alexandrescu (See Website for Email)

unread,
Jul 12, 2004, 6:18:09 PM7/12/04
to
"David Abrahams" <da...@boost-consulting.com> wrote in message
news:u8ydpp...@boost-consulting.com...

> > The innumerable and never-ending smart_ptr construction problems
> > that you, Dave Held, myself, and others have discussed, and that
> > Dave Held and I wrote in CUJ about, are a good start...
>
> But that is a *highly* specialized case dealing with a
> newly-constructed object that takes ownership of a resource (normally
> that job should be encapsulated in smart pointers ;->) and policy
> roles that, while mostly orthogonal, contain some overlap. I've never
> seen a design where those kinds of issues were as knotty. My sense
> was that at least part of the issue was that you were wedded to an
> idea of the original design's "conceptual purity", when that design
> didn't fully account for exception-safety issues. It appeared to me
> that you ended up fighting against the design of C++. The language
> has a "conceptual purity" of its own, and sometimes it makes sense to
> let that tell you how to organize your own designs. Sometimes the
> language is even right ;->.

My view is that we've run into an exception-related problem *inside the
constructor* that could not be solved no matter how we wrote *the
constructor*. That seems like a language design problem, not related to
policies or anything else. I gave details in my CUJ article about that
problem.

> > Also, when I was playing with implementing std::vector, I noticed that
> > implementing the constructors is harder than it should, and could, be.
>
> The answer for std::vector is the same as anywhere else: partition the
> destruction semantics into a common base class or data member. The
> existing language mechanisms deal with that case

If that paragraph ended here, I would have agreed, but then you followed
with:

> cleanly and
> elegantly.

... which I can't agree with :o).


Andrei

Andrei Alexandrescu (See Website for Email)

unread,
Jul 13, 2004, 6:46:55 AM7/13/04
to
"Gerhard Menzl" <gerhar...@spambucket.net> wrote in message
news:40f27b80$1...@news.kapsch.co.at...

> Dave Harris wrote:
>
> > Indeed, we probably need a pre-destructor as well. Suppose we want to
> > enforce a rule that "every object is registered in a global database for
> > the duration of its lifetime", where registration needs fully
constructed
> > objects. The post-constructor registers its object, and if it does not
> > throw the destructor needs to unregister it. If the post-constructor
does
> > throw, then the object has probably not been registered, and the
> > destructor does not need to do anything. So arguably the unregistering
> > needs to be moved to a pre-destructor which is invoked if and only if
the
> > post-constructor was invoked and did not throw. Symmetry.
>
> A few years ago I found myself in a situation where I would have given
> my right ... er ... pinkie's fingernail for a
> post-constructor/pre-destructor facility. The problem involved the
> behaviour of the Observer pattern in multithreaded code.
>
> The full discussion is available here:
>
> http://tinyurl.com/4zvpz

That is very true. Pre-destruction would be necessary as well, but I guess
it's good to focus on explaining post-construction, and only after getting a
foot in the door, tell that pre-destruction would be needed :o).


Andrei

Andrei Alexandrescu (See Website for Email)

unread,
Jul 13, 2004, 6:52:16 AM7/13/04
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:40f2748f...@news.individual.net...

> > When I say that
> > post-constructors (as I suggest could be defined) are "sound", I mean
> > "soundness" in a very precise way. You said flat out that soundness is
> > absent. Now I see that you really meant something like "it could be
abused"
> > which is something very different than that precise meaning I was
referring
> > to.
>
> On the contrary, the meaning "it could be used in a type-safe manner" (by
> very disciplined programmers) is the imprecise meaning.
>
> By that measure assembly language is sound.

It is this kind of statement that is not only just wrong, but also
manipulative of opinion (unintently, I am sure). Consider a casual reader
who takes a look at this thread (which has mostly become an exchange between
two people) and who wants to make an opinion about postconstructors. Then he
skims posts and sees things like "unsound" and "not typesafe" and of course
says, hey, I don't need something that's unsound and not typesafe. Nobody
wants that.

In contrast, consider a parallel conversation in this thread where two
people discuss some techniques and reach the conclusion that one thinks
those techniques are elegant and nice and good enough, while the other
doesn't like them and thinks something else would be more useful. The two
understand each other's point, use terminology correctly, and understand
where their opinions part - namely, in subjective areas that are a matter of
preference and personal judgment.

> If such soundness, whatever it is, is an argument in favor of post-
> constructor-calls then the only alternative must be something even worse
> than assembly language, and you have yet to provide an example.

The example that other posters have given together with me is implementing
the Observer pattern. So the setup is, a constructor of Observer takes a
Subject as an argument:

Observer::Observer(Subject* p) { ... }

Right after completion of full object construction, the Observer should
register itself with the Subject so it can receive notifications, say by
calling:

Subject::Register(Observer*) { ... }

Whenever a registration occurs, the Subject might call into Observer's
virtual functions to tell it its current state, maybe burst some
notifications synchronously, and so on. Obviously, if Register is called
from within Observer's constructors, the scheme won't work.

This problem can be solved by the techniques enumerated in various places in
this thread: object factories, simply requiring Register to be called by
hand after construction, etc. In my opinion, post constructors would be a
particularly elegant solution to this problem.

Other examples involve object brokers, in-memory object databases, and in
general any framework that needs to track objects automatically and know
their exact dynamic types.

> > > [re question apparently forgotten]
> > >
> > > Do you have an example of that exception problem you mentioned, that
> > > you thought an automatic post-constructor-call could in some way help
> > > solving?
> >
> > I've shown an example in another post in the same thread.
>
> I'm sure it most be one snippet or other that I have dismissed as
> irrelevant, unable to see the relevance, so, could you please repeat it
> (not just an article id) and explain clearly (A) what this exception
> problem is, in your opinion, (B) how a post-constructor-call provides a
> simple solution, and (C) why conventional techniques, such as passing data
> or code up the base constructor chain, do not?

David Held and I spend the better part of an article in CUJ (Feb 2004 IIRC)
discussing exception-related problems in constructors. Our particular
discussion has to do with smart pointer construction. I am not going to go
into details here so we don't spread the discussion too thin. The exception
aspect of post-constructors is not very interesting beyond proving that they
are exception-safe, and I believe we are past that step (and I've posted
code twice that illustrates sound postconstructor behavior in the presence
of exceptions).

I'd like focusing the discussion on postconstructor risks and usefulness. I
think Observer is a good example of postconstructor usefulness. I guess
people could disagree about the /degree/ of that usefulness, which is fine.
Someone mentioned they'd give a pinkie toenail or something, and maybe some
other's won't give even the clippings.

Now I'd like to debunk the idea that postconstructors are unsound, and to
that end, here is a challenge.

1. Define "type soundness" and "type safety" so we make sure we are on the
same page.

2. Give a short example that uses the post-constructor facility (with the
semantics I defined) to write an unsound or unsafe program.


Thanks!

Andrei

Alf P. Steinbach

unread,
Jul 13, 2004, 6:56:27 AM7/13/04
to
* Andrei Alexandrescu (See Website for Email):
>
> Wrong.

>
> The Base constructor /has/ executed successfully by the time the
> post-constructor is executing.
>
> I'm not sure where this inference comes from. I believe it comes from the
> wrong antecedents.
>
> This, I believe, also follows from the wrong hypotheses.
>
> Wrong.

With all of the above comments rooted in:


> The constructor constructs objects as it always did like a champ. The
> postconstructor is just like any virtual function - it can be overridden by
> derived classes. So nobody would do construction in the *post*-constructor.
> By definition of the term. :o)

It's _difficult_ to believe that you only know that one very limited
definition, and due to that thought the text was meaningless or wrong, but
as you have stated repeatedly, perhaps there is a terminology problem?

So okay, I'll simply invent new terminology for which there are as yet not
multiple defs, go lawyer-like in descriptions, and do it again.

I define InitEffect for a class T as the total effect of any of its
constructors plus the effect of a post-constructor call, if any, when
these are executed successfully.

I think it should be difficult to accidentally choose another definition
of InitEffect, since as far as I know no other def exists.

Further, I define ContextFreeAssumptions for a class T as the assumptions
that by design shall hold on entry and exit from every public T function,
plus any other T functions designed for ContextFreeAssumptions for T.

I think you must agree that in a sound design the InitEffect(s) should
establish the ContextFreeAssumptions, but if you don't agree please
explain why (an illuminating example would then be nice).

Now, and I know this isn't what you've described but it's a Left Horn


(TM), if an exception from the post() function doesn't automatically cause
object destruction, as with an exception from a constructor, then we have
the case of a potential zombie object, a "vesen" that kills on contact.

Second, the Right Horn (TM), if an exception from the post() function does


cause automatic object destruction, then when implementing a Derived class

constructor, at a point where you know the Base constructor has executed
successfully, you can never know whether Base will later call a post()
function that throws (i.e. whether an exception can come sneaking up on
you). And that means that if Derived's InitEffect involves some costly
(time, memory, user interface effect, network, other) resource acquisition
or side effect it better be done in a post() function in Derived. And


that means, irrespective of whether one wants or otherwise would "need" to

use the post() facility, that all costly InitEffect is in practice moved
to post() functions. And that in turn means that ContextFreeAssumptions


will in general not be fully established by constructors. And that means
extra state-checking everywhere and hence more complex & less maintanable
code and more bug opportunities.

Now I've argued elsewhere in this thread that a post-construction-call


facility would likely be used for such things as acquiring
derived-class-specific resources such as native window handles, and that

is another force that also moves real InitEffect to post() functions.

So in essence the Right Horn (TM) is a more or less complete dismantling
of the type safety currently built into the C++ constructor mechanism
(and it's not a theoretical horn, it's the experience from e.g. MFC).

> > I see only disadvantages with the approach, and no advantages except


> > conceptual simplicity when disregarding the details; what is a concrete
> > example that would not be easily implemented by conventional techniques?
>
> The Observer pattern.

A pattern is by definition not concrete. As I understood it you had
trouble with a particular implementation that used the the Observer
pattern, for which you did not supply enligthening details. Could you
describe that in more concrete terms, please?


PS: in other sub-thread I've yet again asked (please) for a concrete
example or explanation of also the exception problem you thought a
post-constructor-call could help provide a simple solution for, and I hope
you will provide such example in follow-up to that posting, for which I
give this <url: news:40f2748f...@news.individual.net>.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Antoun Kanawati

unread,
Jul 13, 2004, 7:05:21 AM7/13/04
to
Shannon Barber wrote:

> Antoun Kanawati <ant...@comcast.net> wrote in message news:<Y63Hc.26747$JR4.14969@attbi_s54>...
>
> <snip>
>
>>I hope you're not suggesting that we hook up the final vtable,
>>and leave the rest of the object as a random collection of bits.
>
> I do want to hook-up to the final vtable, but after member
> initialization code has executed. See response to Francis Glassborow.

This is already doable in C++. You write very lightweight constructors,
add a virtual "initialize" function which calls its parent versions,
etc...

Gets hairy and uncomfortable with multiple inheritance and virtual
inheritance.

>> > Does someone with more clout than me want to submit it for C++0x?
>>
>>Hey, at least offer this person a free beer, or a dinner. Approaching
>>clout has certain protocols, you know.
>
> Hum, approaching clout sounds much like a date :o)

Only if you need to suck up during dating rituals :)
--
Antoun Kanawati
antou...@comcast.dot.net
[remove .dot and .at before use]

Scott Meyers

unread,
Jul 13, 2004, 4:37:04 PM7/13/04
to
On 12 Jul 2004 17:29:01 -0400, Francis Glassborow wrote:
> If you intend renumbering in the third edition, please ensure that it
> comes with an easy to use mapping of new numbers to old and vice-versa

I already plan to do the best I can, but it's not going to be easy. One
reason is that the material in some current items will get split into
multiple new items. Another is that some existing items will survive in
the new book only as ideas. For example, the current Item 6, which is "use
delete on pointer members in destructors" lives on in the new (tentative)
Item 4, which is (tentatively) "use objects to manage resources." None of
the old text survives in the new Item, but the underlying rational for the
advice (avoiding resource leaks) hasn't changed.

Scott

Francis Glassborow

unread,
Jul 13, 2004, 4:54:37 PM7/13/04
to
In article <2lggt6F...@uni-berlin.de>, "Andrei Alexandrescu (See
Website for Email)" <SeeWebsit...@moderncppdesign.com> writes

>That is very true. Pre-destruction would be necessary as well, but I guess
>it's good to focus on explaining post-construction, and only after getting a
>foot in the door, tell that pre-destruction would be needed :o).

And my idea for 'final' inheritance would cater for that as well. It
would just be an application of the principle that destruction happens
in reverse order to construction.

--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects

David Abrahams

unread,
Jul 13, 2004, 4:56:42 PM7/13/04
to
"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsit...@moderncppdesign.com> writes:

>> Second, the Right Horn (TM), if an exception from the post() function does
>> cause automatic object destruction, then when implementing a Derived class
>> you can never know whether Base is fully constructed or not
>
> Wrong.
>
> The Base has been constructed by its constructor. The constructor does
> construction. The post-constructor offers customizable behavior /after/
> construction.

You may be right technically, Andrei (I don't have an opinion yet),
but I fear that the way you're using language here is going to lead
someone's logic astray. It's an important property that objects are
either successfully constructed, or they are dismantled by the EH
system. Your post-ctors have been injected into the range of code
during which an exception would cause the object to be dismantled.
AFAICT that makes the post-constructor into part of the ctor for all
practical purposes. It's something like a "constructor epilogue",
isn't it?

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Jul 13, 2004, 5:41:27 PM7/13/04
to
* Andrei Alexandrescu (See Website for Email):
> * Alf P. Steinbach:

> > > When I say that
> > > post-constructors (as I suggest could be defined) are "sound", I mean
> > > "soundness" in a very precise way. You said flat out that soundness is
> > > absent. Now I see that you really meant something like "it could be
> abused"
> > > which is something very different than that precise meaning I was
> referring
> > > to.
> >
> > On the contrary, the meaning "it could be used in a type-safe manner" (by
> > very disciplined programmers) is the imprecise meaning.
> >
> > By that measure assembly language is sound.
>
> It is this kind of statement that is not only just wrong, but also
> manipulative of opinion (unintently, I am sure).

Better define what you mean by "sound", then. Good arguments often have
the effect of swaying opinion... :-)



> The example that other posters have given together with me is implementing
> the Observer pattern. So the setup is, a constructor of Observer takes a
> Subject as an argument:
>
> Observer::Observer(Subject* p) { ... }
>
> Right after completion of full object construction, the Observer should
> register itself with the Subject so it can receive notifications, say by
> calling:
>
> Subject::Register(Observer*) { ... }
>
> Whenever a registration occurs, the Subject might call into Observer's
> virtual functions to tell it its current state, maybe burst some
> notifications synchronously, and so on. Obviously, if Register is called
> from within Observer's constructors, the scheme won't work.

Well, I assumed it was something like that and have posted separately
(new thread) on that, with code, not appeared yet in the newsgroup. I
fail to see any fundamental problem. At least that toy program did not
become especially ugly design-wise or code-wise, although as is usual with
pattern-based code it does rely on some conventions (for good measure one
could prevent derivation from the leaf classes -- there are well-known
simple solutions for that).

> This problem can be solved by the techniques enumerated in various places in
> this thread: object factories, simply requiring Register to be called by
> hand after construction, etc. In my opinion, post constructors would be a
> particularly elegant solution to this problem.

Yes, but (1) what _is_ the problem, and (2) elegantness in one particular
setting, when an unsafe tool is wielded by a master, isn't a good argument
to encourage the masses to use that tool everywhere by lowering the price
and heightening the status.



> Other examples involve object brokers, in-memory object databases, and in
> general any framework that needs to track objects automatically and know
> their exact dynamic types.

?


> > I'm sure it most be one snippet or other that I have dismissed as
> > irrelevant, unable to see the relevance, so, could you please repeat it
> > (not just an article id) and explain clearly (A) what this exception
> > problem is, in your opinion, (B) how a post-constructor-call provides a
> > simple solution, and (C) why conventional techniques, such as passing data
> > or code up the base constructor chain, do not?
>
> David Held and I spend the better part of an article in CUJ (Feb 2004 IIRC)
> discussing exception-related problems in constructors. Our particular
> discussion has to do with smart pointer construction. I am not going to go
> into details here so we don't spread the discussion too thin. The exception
> aspect of post-constructors is not very interesting beyond proving that they
> are exception-safe, and I believe we are past that step (and I've posted
> code twice that illustrates sound postconstructor behavior in the presence
> of exceptions).

Is that what you mean by "sound"?

Anyway I didn't intend to ask for a non-problem, sorry about possibly
unclear formulation.

What is the exception _problem_ that you think postconstruction solves?

> Now I'd like to debunk the idea that postconstructors are unsound, and to
> that end, here is a challenge.
>
> 1. Define "type soundness" and "type safety" so we make sure we are on the
> same page.

I think it's up to you to define what you mean "sound", I can't do that
for you.

Regarding type safety I'm referring to breaking the assumptions that
public functions (and other functions designed for that) rely on on entry
and establish on exit -- or else weakening those assumptions so that a
lot of otherwise redundant state checking is required for correctness.
These assumptions are a fundamental and definining part of a type, and
maintaining them is the main and only reason why C++ constructors behave
as they do, and are regarded as type safe . I'd like to say just "class
invariant", but that term has been proven confusing.



> 2. Give a short example that uses the post-constructor facility (with the
> semantics I defined) to write an unsound or unsafe program.

Here you go (after all, it's my favorite example).

I know this isn't how _you_ (or I, for that matter) would use such a
facility.

This is essentially how the same concept, sans compiler support, _is_
used in e.g. MFC, and also in the less archaic WTL library.

struct WindowHandle { ... };
struct ButtonHandle: WindowHandle { ... };

struct Thing { ... }; // Non-instantiable class.

Thing* theThingFor( WindowHandle& aHandle ){ ...; return aThing; }

class Window()
{
protected:
WindowHandle* myHandle;
Thing* myThing;

virtual WindowHandle* newNativeHandle() const
{
return new WindowHandle; // More real: a "native" function.
}

void post()
{
myHandle = newNativeHandle();
myThing = theThingFor( *myHandle );
}

public:
Window() -> post() {}
};

class Button: public Window
{
protected:
virtual WindowHandle* newNativeHandle()
{
//doSomethingWith( *myWindowHandle ); Na na! Doesn't exist!
//doSomethingWith( *myThing ); Ooops! Again!
return new ButtonHandle; // More real: a "native" function.
}

void post()
{
//doSomethingWith( *myWindowHandle ); Na na! Doesn't exist!
//doSomethingWith( *myThing ); Ooops! Again!
Window::post();
}

public:
Button() -> post()
{
//doSomethingWith( *myWindowHandle ); Na na! Doesn't exist!
//doSomethingWith( *myThing ); Ooops! Again!
}
};

> Thanks!

You're welcome.

Now how about that exception problem example, or an explanation at least?

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Andrei Alexandrescu (See Website for Email)

unread,
Jul 13, 2004, 5:44:03 PM7/13/04
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:40f30e49....@news.individual.net...

> I define InitEffect for a class T as the total effect of any of its
> constructors plus the effect of a post-constructor call, if any, when
> these are executed successfully.
>
> I think it should be difficult to accidentally choose another definition
> of InitEffect, since as far as I know no other def exists.
>
> Further, I define ContextFreeAssumptions for a class T as the assumptions
> that by design shall hold on entry and exit from every public T function,
> plus any other T functions designed for ContextFreeAssumptions for T.
>
> I think you must agree that in a sound design the InitEffect(s) should
> establish the ContextFreeAssumptions, but if you don't agree please
> explain why (an illuminating example would then be nice).

I think this is the root of the problem.

The so-called ContextFreeAssumptions ("the object's invariant") are not
established by the InitEffect(s). They are established by the constructor.
The post-constructor has no role in that. In particular, a derived class can
override the postconstructor to do nothing, and that should not affect the
validity of the object; its invariant should hold true.

I understand how starting from the wrong assumption that the postconstructor
has a role in establishing the object's invariant leads one to the reasoning
you posted.


Andrei

Peter

unread,
Jul 13, 2004, 5:45:52 PM7/13/04
to
Hi,
Please allow me to go back to the original problem:

Scott Meyers <Use...@aristeia.com> wrote in message news:<MPG.1b4f973ff...@news.hevanet.com>...
>
> As an example, consider a hierarchy for stock transactions, where
> type-specific information about each transaction should be written to a log
> each time an object is created.

I agree with the solution posted later in the thread - using factory
design pattern. Now another question (which I was pondering about some
time ago) comes to my mind: I would like to call the Logger() method
not just after calling contructor of that class, but after calling
another member functions as well. So I want to be able to specify
(somehow), for which virtual methods from the base class some another
member method (Logger()) should be called after each call of those
methods. How can one achieve this?

Thanks,
Peter

Gerhard Menzl

unread,
Jul 13, 2004, 5:46:38 PM7/13/04
to
Robert Kindred wrote:

> I am afraid I am at a loss for some references, and I will pursue this
> further.

How about Stroustrup, B.: "The C++ Programming Language", pp. 366, for a
start? Or Sutter, H. "Exceptional C++" and "More Exceptional C++", more
or less in their entirety?

> I know that the object can be killed in mid-birth, as you put, but
> I cannot see how the memory can be reclaimed.

Compiler magic, as far as the memory taken by the object under
construction is concerned. To extend that magic to resources allocated
dynamically in the constructor, you need to apply RAII.

Andrei Alexandrescu (See Website for Email)

unread,
Jul 13, 2004, 7:09:24 PM7/13/04
to
"David Abrahams" <da...@boost-consulting.com> wrote in message
news:u3c3wg...@boost-consulting.com...

> "Andrei Alexandrescu \(See Website for Email\)"
<SeeWebsit...@moderncppdesign.com> writes:
> > The Base has been constructed by its constructor. The constructor does
> > construction. The post-constructor offers customizable behavior /after/
> > construction.
>
> You may be right technically, Andrei (I don't have an opinion yet),
> but I fear that the way you're using language here is going to lead
> someone's logic astray. It's an important property that objects are
> either successfully constructed, or they are dismantled by the EH
> system. Your post-ctors have been injected into the range of code
> during which an exception would cause the object to be dismantled.
> AFAICT that makes the post-constructor into part of the ctor for all
> practical purposes. It's something like a "constructor epilogue",
> isn't it?

Intuitively, a post-constructor equates to a function call inserted right
after the user code constructed a full object. Thus, if user code says:

T obj(args);

and T (or a base of it) specifies that postconstructor Post() is to be
called, the code above will be rewritten into:

T obj(args); obj.Post();

(With unnamed temporary objects, the rewrite is less obvious.) Once this
intuition is in place, it is very easy to figure out what exceptions will
do - namely, they'll do exaclty as the rewrite does. If Post() throws an
exception, obj will be destroyed because its constructor has had completed.

The poster who made the connection with AOP and before/after calls hit the
nail on the head.

So conceptually the post-constructor is not part of the constructor in any
way. Its execution begins after all constructors (most derived all the way
to the base) have completed execution successfully.


Andrei

Alf P. Steinbach

unread,
Jul 14, 2004, 5:31:59 AM7/14/04
to
* Andrei Alexandrescu (See Website for Email):
> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:40f30e49....@news.individual.net...
> > I define InitEffect for a class T as the total effect of any of its
> > constructors plus the effect of a post-constructor call, if any, when
> > these are executed successfully.
> >
> > I think it should be difficult to accidentally choose another definition
> > of InitEffect, since as far as I know no other def exists.
> >
> > Further, I define ContextFreeAssumptions for a class T as the assumptions
> > that by design shall hold on entry and exit from every public T function,
> > plus any other T functions designed for ContextFreeAssumptions for T.
> >
> > I think you must agree that in a sound design the InitEffect(s) should
> > establish the ContextFreeAssumptions, but if you don't agree please
> > explain why (an illuminating example would then be nice).
>
> I think this is the root of the problem.
>
> The so-called ContextFreeAssumptions ("the object's invariant") are not
> established by the InitEffect(s). They are established by the constructor.
> The post-constructor has no role in that. In particular, a derived class can
> override the postconstructor to do nothing, and that should not affect the
> validity of the object; its invariant should hold true.

I'm glad you agree that ContextFreeAssumptions should ideally be
established by constructors, and not put off till the post().


> I understand how starting from the wrong assumption that the postconstructor
> has a role in establishing the object's invariant leads one to the reasoning
> you posted.

On the contrary, the assumption that post() will not be used for such
purposes is totally at odds with practical experience from a number
of C++ frameworks, including ObjectWindows, MFC and WTL -- and I did
mention that oddity in the standard library where the post() function
(basic_ios::init) has full responsibility for establishing the
ContextFreeAssumptions -- and this is the "best" code we have...

The assertion of role for the postconstructor is simply circular logic:
"the current mechanism enforces the Right Way (TM), therefore, by
providing a loophole the size of France, incidentally _automating_ and
seemingly _endorsing_ how people have used to work around the enforcement
in the past, the Right Way (TM) will now be the only way used".

Experience dictates the opposite will happen.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Matthias Hofmann

unread,
Jul 14, 2004, 5:34:51 AM7/14/04
to
Shannon Barber <shannon...@myrealbox.com> schrieb in im Newsbeitrag:
de001473.04071...@posting.google.com...

>
> It would break if you "initialized" message as so:
> Derived::Derived()
> {
> message = "I need Help!";
> }
>
> But you shouldn't be doing that ;)

It would also break if you did the following:

class Base
{
int value;

public:
Base() : value( GetValue() ) {}

virtual int GetValue() = 0;
};

But I guess you shouldn't be doing that either ;)

Regards,

Matthias

Andrei Alexandrescu (See Website for Email)

unread,
Jul 14, 2004, 5:37:27 AM7/14/04
to
"Peter" <pnosed...@centrum.sk> wrote in message
news:7bbc5578.04071...@posting.google.com...

> Hi,
> Please allow me to go back to the original problem:
>
> Scott Meyers <Use...@aristeia.com> wrote in message
news:<MPG.1b4f973ff...@news.hevanet.com>...
> >
> > As an example, consider a hierarchy for stock transactions, where
> > type-specific information about each transaction should be written to a
log
> > each time an object is created.
>
> I agree with the solution posted later in the thread - using factory
> design pattern. Now another question (which I was pondering about some
> time ago) comes to my mind: I would like to call the Logger() method
> not just after calling contructor of that class, but after calling
> another member functions as well. So I want to be able to specify
> (somehow), for which virtual methods from the base class some another
> member method (Logger()) should be called after each call of those
> methods. How can one achieve this?

That's a classic AOP motivation. You may want to google for AOP, or look up
www.aspectc.org.

Andrei

Dave Harris

unread,
Jul 14, 2004, 5:48:45 AM7/14/04
to
pnosed...@centrum.sk (Peter) wrote (abridged):

> I would like to call the Logger() method
> not just after calling contructor of that class, but after calling
> another member functions as well. So I want to be able to specify
> (somehow), for which virtual methods from the base class some another
> member method (Logger()) should be called after each call of those
> methods. How can one achieve this?

Make those methods protected or private, and add a public or protected
non-virtual that calls them and then calls Logger().

class Base {
public:
void func1() {
func1_impl();
Logger();
};
private:
virtual void func1_impl() = 0;
};

This is sometimes called a Template Pattern.

(In Common Lisp they have a feature called method combinators, which would
allow you to attach Logger() as an "after" method of func1(), so that you
wouldn't need the func1_impl().)

-- Dave Harris, Nottingham, UK

David Abrahams

unread,
Jul 14, 2004, 5:53:53 AM7/14/04
to
"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsit...@moderncppdesign.com> writes:

> "David Abrahams" <da...@boost-consulting.com> wrote in message
> news:u3c3wg...@boost-consulting.com...
>> "Andrei Alexandrescu \(See Website for Email\)"
> <SeeWebsit...@moderncppdesign.com> writes:
>> > The Base has been constructed by its constructor. The constructor does
>> > construction. The post-constructor offers customizable behavior /after/
>> > construction.
>>
>> You may be right technically, Andrei (I don't have an opinion yet),
>> but I fear that the way you're using language here is going to lead
>> someone's logic astray. It's an important property that objects are
>> either successfully constructed, or they are dismantled by the EH
>> system. Your post-ctors have been injected into the range of code
>> during which an exception would cause the object to be dismantled.
>> AFAICT that makes the post-constructor into part of the ctor for all
>> practical purposes. It's something like a "constructor epilogue",
>> isn't it?
>
> Intuitively, a post-constructor equates to a function call inserted right
> after the user code constructed a full object. Thus, if user code says:
>
> T obj(args);
>
> and T (or a base of it) specifies that postconstructor Post() is to be
> called, the code above will be rewritten into:
>
> T obj(args); obj.Post();
>
> (With unnamed temporary objects, the rewrite is less obvious.)

And with operator new?

> Once this
> intuition is in place, it is very easy to figure out what exceptions will
> do - namely, they'll do exaclty as the rewrite does. If Post() throws an
> exception, obj will be destroyed because its constructor has had
> completed.

But what about

T* x = new T(args)

I presume in that case the memory is still reclaimed when the
post-ctor throws?

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Balog Pal

unread,
Jul 14, 2004, 12:34:13 PM7/14/04
to
"Scott Meyers" <Use...@aristeia.com> wrote in message
news:MPG.1b5bcb516...@news.hevanet.com...

> I'd like to chastize Marshall for renumbering his FAQs (is it really
> helpful that what used to be 23.4 is now 23.7?), but as I'm currently in
> the process of performing significant renumbering of the Items in
Effective
> C++ for a third edition, I'll refrain :-)

Oh no, please don't do that. EC is a thing with tons of references to it
around the worlg and references use item numbers.

What happens if numbers are not going in sequence in the new edition
(explaining why somewhere)? And there's a good cheap supply of fresh,
unused numbers for the new items. ;-)

Or if you mass-rewrite the book, it could just gain a new title instead of
bein EC whatevertd edition. Thus reducing the confusion factor.

Paul

Gerhard Menzl

unread,
Jul 14, 2004, 12:50:18 PM7/14/04
to
Alf P. Steinbach wrote:

> I think it's up to you to define what you mean "sound", I can't do that
> for you.

Considering that it was you you called the idea of post-constructors a
"horror" in the first place, I would say that the burden of definition
should at least be shared by you.

You have just convinced me that constructors and destructors are unsound
and should be removed from the language. Here is my proof:

class Guard
{
public:
Guard (Mutex& mutex)
: myMutex (mutex)
{
myMutex.Release ();
}

~Guard ()
{
myMutex.Acquire ();
}

private:
Mutex& myMutex;
};

I know this isn't how _you_ (or I, for that matter) would use such a

facility, but ...

Seriously, Andrei has stated very clearly that constructors are for
establishing class invariants, and post-constructors are for automating
tasks that should be performed upon completion of construction but do
not affect the class invariants in any way (like, for example, logging).
You seem to argue that people could fail to honour this guideline, hence
the concept is unsound. By that logic, constructors and destructors are
unsound, too (as demonstrated above), and so are loops, conditional
statements, ...

--
Gerhard Menzl

Humans may reply by replacing the obviously faked part of my e-mail
address with "kapsch".

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Andrei Alexandrescu (See Website for Email)

unread,
Jul 14, 2004, 12:50:41 PM7/14/04
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:40f3e6c2....@news.individual.net...

> * Andrei Alexandrescu (See Website for Email):
> > It is this kind of statement that is not only just wrong, but also
> > manipulative of opinion (unintently, I am sure).
>
> Better define what you mean by "sound", then. Good arguments often have
> the effect of swaying opinion... :-)

Type soundness is not something everybody defines to their taste. It has a
clear and agreed upon meaning, and that is the meaning I had attached to it.
You have used the term as well, so I suppose you have a definition of it as
well. Given that your definition is not in sync with the generally accepted
one, I hereby ask you to align it with that definition and implictly retract
your statement that post-constructors are unsound.

> Well, I assumed it was something like that and have posted separately
> (new thread) on that, with code, not appeared yet in the newsgroup. I
> fail to see any fundamental problem. At least that toy program did not
> become especially ugly design-wise or code-wise, although as is usual with
> pattern-based code it does rely on some conventions (for good measure one
> could prevent derivation from the leaf classes -- there are well-known
> simple solutions for that).

I'll keep some grenades for that post :o).

> > This problem can be solved by the techniques enumerated in various
places in
> > this thread: object factories, simply requiring Register to be called by
> > hand after construction, etc. In my opinion, post constructors would be
a
> > particularly elegant solution to this problem.
>
> Yes, but (1) what _is_ the problem,

The problem is that we want to automate virtual function calls after object
construction, and I presented motivation for that.

> and (2) elegantness in one particular
> setting, when an unsafe tool is wielded by a master, isn't a good argument
> to encourage the masses to use that tool everywhere by lowering the price
> and heightening the status.

The tool is safe, so the elegance is in using a safe tool to a good effect.

> > Other examples involve object brokers, in-memory object databases, and
in
> > general any framework that needs to track objects automatically and know
> > their exact dynamic types.
>
> ?

Like Scott's example (quoting):

<<
As I wrote to another poster, is it really so odd to want to perform
something like type-specific logging each time an object in a hierarchy is
created?
>>

> > David Held and I spend the better part of an article in CUJ (Feb 2004
IIRC)
> > discussing exception-related problems in constructors. Our particular
> > discussion has to do with smart pointer construction. I am not going to
go
> > into details here so we don't spread the discussion too thin. The
exception
> > aspect of post-constructors is not very interesting beyond proving that
they
> > are exception-safe, and I believe we are past that step (and I've posted
> > code twice that illustrates sound postconstructor behavior in the
presence
> > of exceptions).
>
> Is that what you mean by "sound"?

Again, what I mean by sound is what sound is agreed upon to mean in the
context of type systems. I am really sorry to sound snooty, but it mildly
puts me off when people throw strong terms around while silently assigning
their own meanings to them.

> What is the exception _problem_ that you think postconstruction solves?

Emailed the Feb 04 CUJ article by Dave Held and myself.

> > Now I'd like to debunk the idea that postconstructors are unsound, and
to
> > that end, here is a challenge.
> >
> > 1. Define "type soundness" and "type safety" so we make sure we are on
the
> > same page.
>
> I think it's up to you to define what you mean "sound", I can't do that
> for you.

No. It is not up to me.

> Regarding type safety I'm referring to breaking the assumptions that
> public functions (and other functions designed for that) rely on on entry
> and establish on exit -- or else weakening those assumptions so that a
> lot of otherwise redundant state checking is required for correctness.
> These assumptions are a fundamental and definining part of a type, and
> maintaining them is the main and only reason why C++ constructors behave
> as they do, and are regarded as type safe . I'd like to say just "class
> invariant", but that term has been proven confusing.

We can go with invariant no problem.

> Here you go (after all, it's my favorite example).

It just so happens it's my favorite too!

> This is essentially how the same concept, sans compiler support, _is_
> used in e.g. MFC, and also in the less archaic WTL library.

Wrong. See below.

> struct WindowHandle { ... };
> struct ButtonHandle: WindowHandle { ... };
>
> struct Thing { ... }; // Non-instantiable class.
>
> Thing* theThingFor( WindowHandle& aHandle ){ ...; return aThing; }
>
> class Window()
> {
> protected:
> WindowHandle* myHandle;
> Thing* myThing;
>
> virtual WindowHandle* newNativeHandle() const
> {
> return new WindowHandle; // More real: a "native" function.
> }
>
> void post()
> {
> myHandle = newNativeHandle();
> myThing = theThingFor( *myHandle );
> }
>
> public:
> Window() -> post() {}
> };

This code is wrong.

It relies on an overridable function to perform initialization. This is a
no-no in today's C++ and is a no-no in C++ with postconstructors. The
correct code is:

class Window()
{
... as above, except post() and the ctor ...
private:


void post()
{
myHandle = newNativeHandle();
myThing = theThingFor(*myHandle);

if (!myHandle || !myThing)
{
delete myHandle;
delete myThing;
throw CannotInitializeHandles();
}
}
public:
Window() -> Window::post()
: myHandle(0), myThing(0)
{
}
};

(It's a straightforward use of the Template Method pattern.) Now Button or
any derived class will override newNativeHandle and everybody's happy.
Anybody can try to override post() until they get blue in the face - it
won't break the invariant. The solution above is as good and elegant a
solution to MFC's dreaded Window handle initialization problem as I ever
hoped for.

Inside post(), the static type of *this is Window but the dynamic type is
the type of the actual object (the vptr has its final value).

By the way, your Button constructor looks like this:

Button() -> post()

In the semantics as I see them, a derived class cannot decide to change what
postconstructor will be invoked.


Andrei

Alex Oren

unread,
Jul 14, 2004, 5:10:40 PM7/14/04
to
al...@start.no (Alf P. Steinbach) wrote in message news:<40f1d0fc...@news.individual.net>...
> Btw., if you have any good idea on how to avoid the "stale" copy problem I
> know that Marshall Cline (the FAQ maintainer) would be very interested to
> hear about it; this problem is one of the reasons why there's currently no
> longer a "get the FAQ zip by e-mail" option as there used to be, and which
> this FAQ was renowned for.

Have a file on the server containing the last update date of the FAQ.
Embed it in local copies using the <OBJECT> OR <IFRAME> tag.

Andrei Alexandrescu (See Website for Email)

unread,
Jul 14, 2004, 5:11:52 PM7/14/04
to
"David Abrahams" <da...@boost-consulting.com> wrote in message
news:uacy37...@boost-consulting.com...

> "Andrei Alexandrescu \(See Website for Email\)"
<SeeWebsit...@moderncppdesign.com> writes:
> > Intuitively, a post-constructor equates to a function call inserted
right
> > after the user code constructed a full object. Thus, if user code says:
> >
> > T obj(args);
> >
> > and T (or a base of it) specifies that postconstructor Post() is to be
> > called, the code above will be rewritten into:
> >
> > T obj(args); obj.Post();
> >
> > (With unnamed temporary objects, the rewrite is less obvious.)
>
> And with operator new?

I knew I forgot to address that just as I hit the 'Send' button, but it was
too late :o). Here's the code that I posted elsewhere in this long and
winding thread:

char* __buf = C::operator new(sizeof C);
C* pObj;
try {
pObj = &__invoke_ctor(C);
}
catch (...) {
C::operator delete(__buf, sizeof(C));
throw;
}
try {
pObj.Post();
}
catch (...) {
pObj->C::~C();
C::operator delete(pObj, sizeof(C));
throw;
}


Andrei

Andrei Alexandrescu (See Website for Email)

unread,
Jul 14, 2004, 5:12:14 PM7/14/04
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:40f45876....@news.individual.net...

> * Andrei Alexandrescu (See Website for Email):
> On the contrary, the assumption that post() will not be used for such
> purposes is totally at odds with practical experience from a number
> of C++ frameworks, including ObjectWindows, MFC and WTL -- and I did
> mention that oddity in the standard library where the post() function
> (basic_ios::init) has full responsibility for establishing the
> ContextFreeAssumptions -- and this is the "best" code we have...

The problems that ObjectWindows, MFC and WTL have would be solved by a
post-construction facility, not exacerbated by it.

> The assertion of role for the postconstructor is simply circular logic:
> "the current mechanism enforces the Right Way (TM), therefore, by
> providing a loophole the size of France, incidentally _automating_ and
> seemingly _endorsing_ how people have used to work around the enforcement
> in the past, the Right Way (TM) will now be the only way used".
>
> Experience dictates the opposite will happen.

How about that logic: "Experience with the lack of a good and needed feature
dictates that the proposed feature won't work". :oD

The assertion for postconstructor's role is simple. There is a problem:
frameworks need to execute code right after construction of a full object
and to have the opportunity of invoking virtual functions. Postconstructors
solve that problem in a sound way.


Andrei

Alf P. Steinbach

unread,
Jul 14, 2004, 6:16:40 PM7/14/04
to
* Andrei Alexandrescu (See Website for Email):
> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:40f3e6c2....@news.individual.net...
> > * Andrei Alexandrescu (See Website for Email):
> > > It is this kind of statement that is not only just wrong, but also
> > > manipulative of opinion (unintently, I am sure).
> >
> > Better define what you mean by "sound", then. Good arguments often have
> > the effect of swaying opinion... :-)
>
> Type soundness

A partial definition, thanks.


> Given that your definition is not in sync with the generally accepted
> one, I hereby ask you to align it with that definition and implictly retract
> your statement that post-constructors are unsound.

That's enough.


> > Well, I assumed it was something like that and have posted separately
> > (new thread) on that, with code, not appeared yet in the newsgroup. I
> > fail to see any fundamental problem. At least that toy program did not
> > become especially ugly design-wise or code-wise, although as is usual with
> > pattern-based code it does rely on some conventions (for good measure one
> > could prevent derivation from the leaf classes -- there are well-known
> > simple solutions for that).
>
> I'll keep some grenades for that post :o).

It is one of two "problems" you have claimed constitute a rationale for
post-constructors (here disregarding the claim about billions others
as mere wishfullness), and since the other one is presumably
non-discussable due to upcoming article let's concentrate on this one.

I have repeatedly asked for a concrete example, and I now do so again.

Just post your thoughts, don't hold back any "grenades".


> The problem is that we want to automate virtual function calls after object
> construction, and I presented motivation for that.

That is a (bad) solution to some perceived problem.

What is the problem?



> > and (2) elegantness in one particular
> > setting, when an unsafe tool is wielded by a master, isn't a good argument
> > to encourage the masses to use that tool everywhere by lowering the price
> > and heightening the status.
>
> The tool is safe, so the elegance is in using a safe tool to a good effect.

Wishing it were so, stating repeatedly it is so, doesn't make it so.


> > > Other examples involve object brokers, in-memory object databases, and
> in
> > > general any framework that needs to track objects automatically and know
> > > their exact dynamic types.
> >
> > ?
>
> Like Scott's example (quoting):
>
> <<
> As I wrote to another poster, is it really so odd to want to perform
> something like type-specific logging each time an object in a hierarchy is
> created?
> >>

That topic has been discussed and many simple solutions put forward.

I'll add yet another one: a creation logger that if need be discards base
class creation log entries if the requirement is that they should not
be in the log.

Often the most appropriate solution to the problem "discard something" is
to discard something.


> > > code twice that illustrates sound postconstructor behavior in the
> > > presence of exceptions).
> >
> > Is that what you mean by "sound"?
>
> Again, what I mean by sound is what sound is agreed upon to mean in the
> context of type systems.

What do you mean by type system soundness in the presence of exceptions,
in this case?


> I am really sorry to sound snooty, but it mildly
> puts me off when people throw strong terms around while silently assigning
> their own meanings to them.

I agree with that... ;-)

And especially when they refuse to clarify what they mean.


> > What is the exception _problem_ that you think postconstruction solves?
>
> Emailed the Feb 04 CUJ article by Dave Held and myself.

I have looked at it. AFAICS there is no such problem, but there is an
apparent opportunity to improve the implementation by another well-known
and conventional technique. E-mailed that suggestion today to you.


> > > 1. Define "type soundness" and "type safety" so we make sure we
> > > are on the same page.
> >
> > I think it's up to you to define what you mean "sound", I can't do that
> > for you.
>
> No. It is not up to me.

Of course it is. You're claiming property X without defining it or
even giving a context. In this latest reply you have finally provided
a context, and it was not the one implied by earlier postings.


> > Here you go (after all, it's my favorite example).
>
> It just so happens it's my favorite too!
>
> > This is essentially how the same concept, sans compiler support, _is_
> > used in e.g. MFC, and also in the less archaic WTL library.
>
> Wrong. See below.

Here we can say "yes, it is!" and "no it isn't!" till we're blue in the
face.

I maintain that that is the way e.g. MFC works.

If you need examples I'll provide them, but it's a lot of detail.


> > struct WindowHandle { ... };
> > struct ButtonHandle: WindowHandle { ... };
> >
> > struct Thing { ... }; // Non-instantiable class.
> >
> > Thing* theThingFor( WindowHandle& aHandle ){ ...; return aThing; }
> >
> > class Window()
> > {
> > protected:
> > WindowHandle* myHandle;
> > Thing* myThing;
> >
> > virtual WindowHandle* newNativeHandle() const
> > {
> > return new WindowHandle; // More real: a "native" function.
> > }
> >
> > void post()
> > {
> > myHandle = newNativeHandle();
> > myThing = theThingFor( *myHandle );
> > }
> >
> > public:
> > Window() -> post() {}
> > };
>
> This code is wrong.

Of course it's wrong: that was the point.


> It relies on an overridable function to perform initialization. This is a
> no-no in today's C++ and is a no-no in C++ with postconstructors.

Re-quoting:

> The problem is that we want to automate virtual function calls after object
> construction, and I presented motivation for that.

In the first quote, overridable function is no-no for initialization. In
the second it is a rationale for having post-constructors, so they _will_
be used, and in the source text called from constructors. Is this good?

Also, and I just can't believe this!, below you then use an overridable
function to perform initialization and state that it's as good and elegant
a solution you've ever seen.

But presumably you have a special meaning of "initialization"?

If so, exactly what is it that an overridable function is not to perform?

And if no special definition of "initialization", compare the first Andrei
quote above with the quoted Andrei code and comment right below.


> The correct code is:
>
> class Window()
> {
> ... as above, except post() and the ctor ...
> private:
> void post()
> {
> myHandle = newNativeHandle();
> myThing = theThingFor(*myHandle);
> if (!myHandle || !myThing)
> {
> delete myHandle;
> delete myThing;
> throw CannotInitializeHandles();
> }
> }
> public:
> Window() -> Window::post()
> : myHandle(0), myThing(0)
> {
> }
> };
>
> (It's a straightforward use of the Template Method pattern.) Now Button or
> any derived class will override newNativeHandle and everybody's happy.
> Anybody can try to override post() until they get blue in the face - it
> won't break the invariant.

newNativeHandle can break the ContextFreeAssumptions.

(I'm using clear terminology defined in other sub-thread here in case
"invariant" suddenly means only what the constructor established, which
would make above a meaningless statement that can't be argued against.)


> The solution above is as good and elegant a
> solution to MFC's dreaded Window handle initialization problem as I ever
> hoped for.

Then you don't hope for much... :-o You've added error checking in
post(), OK, I left out error checking there for clarity; it's not
relevant. Second, you've added qualification of the post() call; what
for, it isn't virtual? Third, you've added zero-initialization in the
constructor, and those are the only changes.

Zero-initialization is only meaningful if the intention is that other code
should be _checking_ whether these members have been, uh, "constructed".

So you have introduced a lot of unnecessary state-checking.

And the ConstructorEffect is now only to establish (myNativeHandle == 0).
So what _would_ be the class invariant -- by anyone's terminology --
in a more sound design, is in your code only established by the
post-constructor, using a virtual call. That's very, very, very bad, and
you call it "good"?

And yes, there is at least one easy way to achieve type-safe (in every
sense) initialization without post() calls, namely by passing a handle
provider object up the base class constructor chain.


> Inside post(), the static type of *this is Window but the dynamic type is
> the type of the actual object (the vptr has its final value).
>
> By the way, your Button constructor looks like this:
>
> Button() -> post()
>
> In the semantics as I see them, a derived class cannot decide to change what
> postconstructor will be invoked.

OK, didn't know that (it was never specified, yet another late-bound
thing). Does that mean that post(), as you see it, is _always_ virtual?
Or does that mean that a derived class cannot add post-constructor calls?

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Jul 14, 2004, 6:46:46 PM7/14/04
to
* Gerhard Menzl:

> Alf P. Steinbach wrote:
>
> > I think it's up to you to define what you mean "sound", I can't do that
> > for you.
>
> Considering that it was you you called the idea of post-constructors a
> "horror" in the first place, I would say that the burden of definition
> should at least be shared by you.

Nope. I'm not one who argues by way of word-play.



> You have just convinced me that constructors and destructors are unsound
> and should be removed from the language. Here is my proof:
>
> class Guard
> {
> public:
> Guard (Mutex& mutex)
> : myMutex (mutex)
> {
> myMutex.Release ();
> }
>
> ~Guard ()
> {
> myMutex.Acquire ();
> }
>
> private:
> Mutex& myMutex;
> };
>
> I know this isn't how _you_ (or I, for that matter) would use such a
> facility, but ...

Constructors do enforce some rules, so that if you try to break them
you won't be able to.

Post-constructors do not enforce those rules.

My example concerned those rules, whereas your example concerns only what
constructors (or any language feature) can not help prevent you from
doing, and so your example is 100% irrelevant and 50% misleading.


> Seriously, Andrei has stated very clearly that constructors are for
> establishing class invariants, and post-constructors are for automating
> tasks that should be performed upon completion of construction

Stating that goto should only be used in structured ways, and therefore
is as safe as loop and selection constructs, don't make that reality.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Jul 15, 2004, 6:19:56 AM7/15/04
to
* Andrei Alexandrescu (See Website for Email):
> "Alf P. Steinbach" <al...@start.no> wrote in message
> news:40f45876....@news.individual.net...
> > * Andrei Alexandrescu (See Website for Email):
> > On the contrary, the assumption that post() will not be used for such
> > purposes is totally at odds with practical experience from a number
> > of C++ frameworks, including ObjectWindows, MFC and WTL -- and I did
> > mention that oddity in the standard library where the post() function
> > (basic_ios::init) has full responsibility for establishing the
> > ContextFreeAssumptions -- and this is the "best" code we have...
>
> The problems that ObjectWindows, MFC and WTL have would be solved by a
> post-construction facility, not exacerbated by it.

In what way(s) would a language supported post-constructor avoid the
current real problems of post-construction in those frameworks?

> > The assertion of role for the postconstructor is simply circular logic:
> > "the current mechanism enforces the Right Way (TM), therefore, by
> > providing a loophole the size of France, incidentally _automating_ and
> > seemingly _endorsing_ how people have used to work around the enforcement
> > in the past, the Right Way (TM) will now be the only way used".
> >
> > Experience dictates the opposite will happen.
>
> How about that logic: "Experience with the lack of a good and needed feature
> dictates that the proposed feature won't work". :oD

Experience with (ab)use of the same mechanism, just without language
support.

In what way(s) would a language supported post-constructor avoid the
current real problems of post-construction in those frameworks?

How can you possibly think that language-supported automation of such
calls won't make the technique more used, and exacerbate these problems?


> The assertion for postconstructor's role is simple. There is a problem:
> frameworks need to execute code right after construction of a full object
> and to have the opportunity of invoking virtual functions.

Example?

(Umpteen'th request.)

> Postconstructors solve that problem in a sound way.

First: AFAIK there is no such problem, a.k.a need.

You need to demonstrate that there is; just saying it is so doesn't make
it so. Example? (Umpteen+1'th request.)

Second: in what way(s) would a language supported post-constructor avoid
the current _real_ problems of post-construction in those frameworks?

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Andrei Alexandrescu (See Website for Email)

unread,
Jul 15, 2004, 6:32:00 AM7/15/04
to
"Alf P. Steinbach" <al...@start.no> wrote in message
news:40f56ec8...@news.individual.net...

> * Andrei Alexandrescu (See Website for Email):
> > "Alf P. Steinbach" <al...@start.no> wrote in message
> > news:40f3e6c2....@news.individual.net...
> > > * Andrei Alexandrescu (See Website for Email):
> > > > It is this kind of statement that is not only just wrong, but also
> > > > manipulative of opinion (unintently, I am sure).
> > >
> > > Better define what you mean by "sound", then. Good arguments often
have
> > > the effect of swaying opinion... :-)
> >
> > Type soundness
>
> A partial definition, thanks.
>
>
> > Given that your definition is not in sync with the generally accepted
> > one, I hereby ask you to align it with that definition and implictly
retract
> > your statement that post-constructors are unsound.
>
> That's enough.

I guess I made my point. I keep on asking for what you mean by lack of
soundness, and you keep on asking me back what I mean by soundness. Reminds
me of that joke with the reporter and the politician: "Is it true that you
always reply a question with another question?" "Who told you that?" :o)

I am sorry, I can only infer you do not have a clear notion of type
soundness, and that you use the term colloquially.

Simply put, soundness of a type system means that data is always read in the
same format than it was written in, and that all expressions do not get
stuck (call of a non-existing procedure etc.). Soundness precludes access to
uninitialized (or already freed) memory, things like casts or memcpy, or
off-bounds array access. Most soundness is proved statically, but things
like array bound access are hard to check statically in useful ways.

Within the context of C++, a feature has type soundness if it does not add
to the already-existing unsafe aspects of C++ (readers of this newsgroup
might be glad to hear that Herb Sutter and I dedicated one special section
to enumerating and discussing the unsound features of C++ in the upcoming
"C++ Coding Standards" book, see
http://www.amazon.com/exec/obidos/tg/detail/-/0321113586/qid=1089844542/sr=8-1/ref=sr_8_xs_ap_i1_xgl14/002-6053397-6007261?v=glance&s=books&n=50
7846).

> I have repeatedly asked for a concrete example, and I now do so again.

Should I take it you refuse to read over the examples I give?

> Also, and I just can't believe this!, below you then use an overridable
> function to perform initialization and state that it's as good and elegant
> a solution you've ever seen.

Of course you can't believe it. When two people have a conversation, the
sheer fact that one can't make sense of the other's words is a good
indicator of a misunderstanding. In contrast, I will take a modesty-lacking
route in saying that I understand what you say, and where the confusions
stand. In this case it looks like you did not understand the changes I made
to your windowing code to render it correct.

Your code was:

class Window { Window() -> post() { ... } };

and my code was:

class Window { Window() -> Window::post() { ... } };

I had specified earlier that the postconstructor call is executed right
after the full object is built. That is, say the user defines:

Button myButton;

Then the code expands to (according to my postconstructor):

Button myButton; myButton.Window::post();

which forces a call to Window's version of post, even though Button overrode
it. That is an important detail.

> newNativeHandle can break the ContextFreeAssumptions.

newNativeHandle doesn't need to be public, and by the way you defined
ContextFreeAssumptions, it doesn't interfere with them.

> Then you don't hope for much... :-o You've added error checking in
> post(), OK, I left out error checking there for clarity; it's not
> relevant. Second, you've added qualification of the post() call; what
> for, it isn't virtual?

Here's where the misunderstanding becomes clear. The added qualification was
an important change. Again, please understand that the qualifier that I've
added forces Window::post() to be called, and not any override of it. In
many other cases we want to call the override, and let derived classes do
whatever. In this case, the Template Method pattern works best.

> Third, you've added zero-initialization in the
> constructor, and those are the only changes.

I did so such that newNativeHandle doesn't access uninitialized memory.
Given newNativeHandle's charter, it oughtn't touch them anyway, but I put
the initialization there for, um... soundness. :oD


Andrei

Alexander Terekhov

unread,
Jul 15, 2004, 6:34:48 AM7/15/04
to

Gerhard Menzl wrote:
[...]

> You have just convinced me that constructors and destructors are unsound
> and should be removed from the language. Here is my proof:
>
> class Guard
> {
> public:
> Guard (Mutex& mutex)
> : myMutex (mutex)
> {
> myMutex.Release ();
> }
>
> ~Guard ()
> {
> myMutex.Acquire ();
> }
>
> private:
> Mutex& myMutex;
> };
>
> I know this isn't how _you_ (or I, for that matter) would use such a
> facility, but ...
>
> Seriously,

I for one do use "release_guards". An example can be found here:

http://terekhov.de/DESIGN-futex-CV.cpp

~futex_condvar() {
mutex::guard guard(m_mutex);
assert(m_waiters[0] == m_wakeups);
while (m_waiters[0]) {
int ftx = m_futex = EOC();
mutex::release_guard release_guard(guard);
cancel_off_guard no_cancel;
m_futex.wait(ftx);
}
}

> Andrei has stated very clearly that constructors are for
> establishing class invariants, and post-constructors are for automating

> tasks that should be performed upon completion of construction ...

and pre-destructors should undo those post-contruction tasks (1st
phase of destruction; same order as with destructors; if post-ctor
throws, pre-dtor(s) of base class(es) are called and pre-dtor(s)
of derived classes are not called, of course). Oder?

regards,
alexander.

--
http://groups.google.com/groups?selm=3E7B4DB0.5AB0054E%40web.de

Gerhard Menzl

unread,
Jul 15, 2004, 6:40:44 AM7/15/04
to
Alf P. Steinbach wrote:

> Nope. I'm not one who argues by way of word-play.

You are trying to shift the burden of proof from the one who criticizes
to the one who is being criticized.

> Constructors do enforce some rules, so that if you try to break them
> you won't be able to.
>
> Post-constructors do not enforce those rules.

Of course not. It's not the intention behind them.

> Stating that goto should only be used in structured ways, and
> therefore is as safe as loop and selection constructs, don't make that
> reality.

Stating that constructors should only be used to allocate resources and
establish class invariants doesn't make it reality.

Your argument against post-constructors boils down to "but it could be
abused, just look what MFC is doing". If this were valid, you would have
a pretty strong case against computer programs written by humans in
general. Since I don't think that this is what you are trying to get at,
you need to show conclusively that post-constructors are error-prone by
nature. I haven't seen presented any evidence for this.


--
Gerhard Menzl

Humans may reply by replacing the obviously faked part of my e-mail
address with "kapsch".

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Jul 15, 2004, 7:47:08 PM7/15/04
to
* Gerhard Menzl:

> Alf P. Steinbach wrote:
>
> > Nope. I'm not one who argues by way of word-play.
>
> You are trying to shift the burden of proof from the one who criticizes
> to the one who is being criticized.
>
> > Constructors do enforce some rules, so that if you try to break them
> > you won't be able to.
> >
> > Post-constructors do not enforce those rules.
>
> Of course not. It's not the intention behind them.
>
> > Stating that goto should only be used in structured ways, and
> > therefore is as safe as loop and selection constructs, don't make that
> > reality.
>
> Stating that constructors should only be used to allocate resources and
> establish class invariants doesn't make it reality.
>
> Your argument against post-constructors boils down to "but it could be
> abused, just look what MFC is doing". If this were valid, you would have
> a pretty strong case against computer programs written by humans in
> general. Since I don't think that this is what you are trying to get at,
> you need to show conclusively that post-constructors are error-prone by
> nature. I haven't seen presented any evidence for this.

Happily constructors in C++ _do_ have some restrictions on what can
be done.

Postconstructors don't have those restrictions.

Think.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alf P. Steinbach

unread,
Jul 15, 2004, 7:50:54 PM7/15/04
to
* Andrei Alexandrescu (See Website for Email):
> * Alf P. Steinbach:

> > * Andrei Alexandrescu (See Website for Email):
> > > * Alf P. Steinbach:

> > > > * Andrei Alexandrescu (See Website for Email):
> > > > > It is this kind of statement that is not only just wrong, but also
> > > > > manipulative of opinion (unintently, I am sure).
> > > >
> > > > Better define what you mean by "sound", then. Good arguments often
> > > > have the effect of swaying opinion... :-)
> > >
> > > Type soundness
> >
> > A partial definition, thanks.
> >
> > > Given that your definition is not in sync with the generally accepted
> > > one, I hereby ask you to align it with that definition and implictly
> > > retract your statement that post-constructors are unsound.
> >
> > That's enough.
>
> I guess I made my point.

When I wrote "That's enough" I meant enough of those rhetorical techniques
and insinuations, like quoted above that I should be "manipulative" and
should have given a definition "not in sync with the general accepted one"
(I have not), and your use of undefined terms and terms with multiple
meanings, sometimes at the end, when pressed on the point, choosing an
inappropriate one that makes your statements _literally_ correct, although
rather meaningless. I find use of such insinuations etc. a dishonest
practice, and repulsive. And I note a pattern, that very often use of
such techniques is accompanied by a cheerful insinuation that I use them.

Now type system soundness -- and that was not what you wrote originally,
you had to pressed repeatedly on the point to get even that far -- is not
a context free term; for any particular language and language construct
the precise meaning depends on the definitions adopted.

Yet there is an intuitive meaning, which any more precise definition
should implement. By applying definitions and words that are meaningful
for the current restricted constructor mechanism (here I am giving you the
benefit of doubt in spite of all the rhetoric and in spite of a childish
and inappropriate definition given) _unadapted_ for the new unrestricted
postconstructor mechanism, essentially that the restricted old part is
still OK even when it's not used for anything but zero-initialization, you
are at odds with the intuitive meaning and have negated the practical
utility. As David Abrahms wrote earlier to you, "I fear that the way


you're using language here is going to lead someone's logic astray."

> > Then you don't hope for much... :-o You've added error checking in
> > post(), OK, I left out error checking there for clarity; it's not
> > relevant. Second, you've added qualification of the post() call; what
> > for, it isn't virtual?

> Here's where the misunderstanding becomes clear. The added qualification was
> an important change. Again, please understand that the qualifier that I've
> added forces Window::post() to be called, and not any override of it. In
> many other cases we want to call the override, and let derived classes do
> whatever. In this case, the Template Method pattern works best.

The original code used the Template Pattern; I thought the code was so
simple, short and self-evident that mentioning that would not bring
additional clarity.

Now you're arguing on the basis of previously undiclosed details of the
syntax of your proposed language feature, saying I used this undefined
feature syntactically incorrectly, but _implying_ rather more; so what?

Your post() function, which you described as "as good and elegant as [you]
could ever hope for" calls a virtual function to do initialization, which
you have stated is "no no" -- and _that_ "no no" statement I agree with.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

[ See http://www.gotw.ca/resources/clcm.htm for info about ]

Alexander Terekhov

unread,
Jul 15, 2004, 7:52:18 PM7/15/04
to

"Andrei Alexandrescu (See Website for Email)" wrote:
[...]

> Button myButton;
>
> Then the code expands to (according to my postconstructor):
>
> Button myButton; myButton.Window::post();

Yeah,

#include <iostream>
#include <typeinfo>

struct Base {

Base() {
std::cout << "Base ctor\n";
}

virtual ~Base() {
std::cout << "Base dtor\n";
}

void post_ctor() {
std::cout << "Base post-ctor: " << typeid(*this).name() << "\n";
}

void pre_dtor() {
std::cout << "Base pre-dtor: " << typeid(*this).name() << "\n";
}

};

struct Derived : Base {

Derived() {
std::cout << "Derived ctor\n";
}

~Derived() {
std::cout << "Derived dtor\n";
}

void post_ctor() {
std::cout << "Derived post-ctor: " << typeid(*this).name() << "\n";
}

void pre_dtor() {
std::cout << "Derived pre-dtor: " << typeid(*this).name() << "\n";
}

};

struct MostDerived : Derived {

MostDerived() {
std::cout << "MostDerived ctor\n";
}

~MostDerived() {
std::cout << "MostDerived dtor\n";
}

void post_ctor() {
std::cout << "MostDerived post-ctor: " << typeid(*this).name() << "\n";
}

void pre_dtor() {
std::cout << "MostDerived pre-dtor: " << typeid(*this).name() << "\n";
}

};

template< class B2, class B1 >
struct post_pre : B2 {

post_pre() : B2() {
B1::post_ctor();
}

~post_pre() {
B1::pre_dtor();
}

};

int main() {
try {
// MostDerived o;
post_pre<post_pre<post_pre<MostDerived, Base>, Derived>, MostDerived> o;
}
catch(...) { }
}

We just need "auto-generation" with transparency/invisibility for
this pre_post<>-chain-of-invokation-"classes" triggered by use of
post-ctor and/or pre-dtor in some class (I don't care about the
syntax). Oder?

regards,
alexander.

Andrei Alexandrescu (See Website for Email)

unread,
Jul 15, 2004, 8:10:05 PM7/15/04
to
"Alexander Terekhov" <tere...@web.de> wrote in message
news:40F5C15F...@web.de...

> and pre-destructors should undo those post-contruction tasks (1st
> phase of destruction; same order as with destructors; if post-ctor
> throws, pre-dtor(s) of base class(es) are called and pre-dtor(s)
> of derived classes are not called, of course). Oder?

Ja, das ist korrekt :o).

To keep things simple, I think it is enough that once a base class specifies
a postconstructor, derived classes don't pile more on top of it.

Tschuess,

Andrei

It is loading more messages.
0 new messages