Topic: proposal - another form of new statement


Author: dave@boost-consulting.com (David Abrahams)
Date: Mon, 8 Sep 2003 14:22:59 +0000 (UTC)
Raw View
brok@rubikon.pl (Bronek Kozicki) writes:

> On Wed, 3 Sep 2003 19:17:25 +0000 (UTC), David Abrahams wrote:
>>> [...] If there's any non-virtual,
>>> publicly available destructor, there's risk that object being createed
>>> will be destroyed using pointer, which static type will point to unsafe
>>> base class, thus invoking undefined behaviour. My proposal is to make
>>> such program ill-formed,
> [...]
>> Anyway, I'd prefer to see a type trait which allows me to *check* the
>> condition you're interested in than a new core language extension
>> which fails compilation when the condition is violated.
>
> thanks for reminding type traits. I agree that it would give more
> flexibility to programmers. However I think that for this purpose there
> should be something else than is_polymorphic, because it's value is
> "defined to be true if T is a polymorphic class (10.3)", and per 10.3
> class is polymorphic, it it contains *any* virtual function. It does not
> allow to verify that class has *all it's publicy available destructors
> virtual* (which is main point of my proposal).

That's why I said it was an approximation.

> Do you think, that it would be possible to add another trait (like
> is_virtually_destructible) to proposal N1424 ?

I think it's too late to modify that proposal, though the committee
can change what actually goes in the TR or standard during drafting.
I suggest submitting a separate paper (in the next week or so, before
the Kona pre-meeting mailing deadline) proposing your trait for the
TR.  One thing to keep in mind is that almost all of the traits in
N1424 have existing implementations and usage experience.  You'll want
to address the fact that is_virtually_destructible does not, and try
to justify its implementation practicality and broad usefulness in
the absence of actual experience.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

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





Author: brok@rubikon.pl (Bronek Kozicki)
Date: Mon, 8 Sep 2003 17:15:30 +0000 (UTC)
Raw View
On Fri, 5 Sep 2003 08:30:44 +0000 (UTC), Maciej Sobczak wrote:
> Bronek Kozicki wrote:
>> I'd like to propose another form of "new" statement, which will can be
>> used to gain guarantee that object being created has virtual destructors
>> (in all its public base classes), and thus can be safely destroyed when
>> used polymorphicaly.
>
> I do not understand why you want to make it illegal to *create* such an
> object. The problem is in the point of destruction, not in the point of
> creation.

You are right, but as you noticed there is no complete solution to
detect this problem at the point of object destruction. There is one
half-solution, namely to make following ill-formed:

> - the pointer is to polymorphic class
> - the static type of pointer is to a class that has no virtual destructor

.... but it would change meaning of already existing code (from
"undefined behaviour" to "compilation error"). I do not believe that my
humble person could be able to convince Committee to make such change.
And of more importantly : this is only half of the problem. The other
half is deleting polymorphic object through pointer to its
non-polymorphic base class, as you already noticed. This cannot be
detected in one place. There could be some half solution, like ...

class base{/* non-polymorphic */};
class derived : public base {virtual ~derived(){} /* polymorphic */};

myDerived* = new derived(); // line 1;
base * myBase = myDerived; // line 2
delete myBase; // line 3
// delete myDerived; // line 4

.... make line 2 illegal, but it is much too serious change in language;
Compiler has no means to detect that pointer initialized in line 2 has
been deleted in line 3.

There is also another thing one must think about. Part of program which
created object (line 1 above, and sorroundig class/function) may own it
for its whole lifetime, and thus be responsible for its destruction. If
this is the situation, there's nothing wrong with line 2 above (and line
3 would never happen). There is important assumption: line 2 is NOT
passing ownership of pointed object; thus object can still be destroyed
through correct, polymorphic pointer. Such program might not be nice,
but is perfectly correct (according to C++ language rules). Things are
much different if line 2 is passing ownership of the object (presumably
to some different part of program). You are one step away from disaster.
Potential solution would be to:
- change behaviour of line 2  (and make it illegal?)
- propose another form of cast statement, which could be used to make
this program ill-formed (ie. "when pointer to polymorphic object is used
to initilize, or assigned to, pointer to non-polymorphic object of its
base class"). I do not think that Committee would be happy to accept
another form of cast, like "safe_cast<base*> " ;
- make deletion of pointers to non-polymorphic objects illegal (just
kidding)

summa summarum - I do not think that there could be complete protection
against undefined behaviour in place of object destruction, and I do not
believe that incomplete soultion could be accepted by Committee.
Complete solution would require change of meaning of existing code
and/or more changes to the language.

My proposal is based on assumption that "when object is being created,
there must some idea on how its going to be destroyed". This idea in
fact (or lack of it) is the main reason why destructor might be virtual.
Proposed extension (and/or type trait, as suggested by Dave Abrahams in
this thread) would really make simple solution. And what's more
important: this would be really complete solution, without smallest
change of meaning of existing code.

I do not agree with David though, that is_polymorphic type trait could
be used here. That's because its value is "defined to be true if T is a
polymorphic class (10.3)" (per proposal N1424), and per 10.3 class is
polymorphic, it it contains *any* virtual function. However, inspired by
Dave's post, I suggest creating another trait, namely:

* is_polymorphicaly_destructible (I'd love to make this name shorter,
but have nothing at hand), and its meaning would be : "value is defined
to be true, if all publicly available destructors of class (including
its public base classes) are virtual".

This, together with static_assert could offer really complete solution.
I'd like to go one step futher, and propose extension to new, like

new (polymorphic)
and
new (nothrow, polymorphic)

.... implemented in terms of is_polymorphicaly_destructible. If accepted,
this could be used to make our programs safer, without smallest change
of meaning of existsting code, and (what's important) consitent with
existing "new (nothrow)" and proposed "type_traits" parts of language,
thus easy to learn and use.

> But hey - who publicly derives from non-polymorphic classes? :)

have you ever wrote something similar to:

class MyClass : public boost::noncopyable  // ...

?

Kind regards


B.

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





Author: brok@rubikon.pl (Bronek Kozicki)
Date: Tue, 2 Sep 2003 19:48:28 +0000 (UTC)
Raw View
I'd like to propose another form of "new" statement, which will can be
used to gain guarantee that object being created has virtual destructors
(in all its public base classes), and thus can be safely destroyed when
used polymorphicaly.

problem:

template <typename BASE, typename DERIVED>
BASE* create(int i) {return new DERIVED(i);}

base* o = create<base, derived>(3);
//...
delete o; // correct or not?

In this example function "create" might be simplified part of some
factory. The problem is that if function "create" is part of template
class (or is template function itself), it might be misused to create
objects which does not have virtual destructor (and thus was not
designed to be used polymorphicaly). There's no way to gain information
in compile-time that template parameter DERIVED or BASE has not been
designed to be used polymorphicaly. In this case destruction of object
"o;" will invoke undefined behaviour. It would much better to receive
compilation error, assuming some special form of new has been used, like
following (reusing keyword "explicit", just as an example):
return new explicit DERIVED(i)

When object is being created, its type must be complete, as well as its
all base classes must be known (in order to create appropriate order of
constructors). Thus it should be not difficult to verify that it's all
publicly available destructors are virtual. If there's any non-virtual,
publicly available destructor, there's risk that object being createed
will be destroyed using pointer, which static type will point to unsafe
base class, thus invoking undefined behaviour. My proposal is to make
such program ill-formed, and issue diagnostic in point of code where
object is being created using proposed special form of new statement.

I hope that this proposal is reasonable.


B.

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





Author: dave@boost-consulting.com (David Abrahams)
Date: Wed, 3 Sep 2003 19:17:25 +0000 (UTC)
Raw View
brok@rubikon.pl (Bronek Kozicki) writes:

> I'd like to propose another form of "new" statement, which will can be
> used to gain guarantee that object being created has virtual destructors
> (in all its public base classes), and thus can be safely destroyed when
> used polymorphicaly.
>
> problem:
>
> template <typename BASE, typename DERIVED>
> BASE* create(int i) {return new DERIVED(i);}
>
> base* o = create<base, derived>(3);
> //...
> delete o; // correct or not?
>
> In this example function "create" might be simplified part of some
> factory. The problem is that if function "create" is part of template
> class (or is template function itself), it might be misused to create
> objects which does not have virtual destructor (and thus was not
> designed to be used polymorphicaly). There's no way to gain information
> in compile-time that template parameter DERIVED or BASE has not been
> designed to be used polymorphicaly. In this case destruction of object
> "o;" will invoke undefined behaviour. It would much better to receive
> compilation error, assuming some special form of new has been used, like
> following (reusing keyword "explicit", just as an example):
> return new explicit DERIVED(i)
>
> When object is being created, its type must be complete, as well as its
> all base classes must be known (in order to create appropriate order of
> constructors). Thus it should be not difficult to verify that it's all
> publicly available destructors are virtual. If there's any non-virtual,
> publicly available destructor, there's risk that object being createed
> will be destroyed using pointer, which static type will point to unsafe
> base class, thus invoking undefined behaviour. My proposal is to make
> such program ill-formed, and issue diagnostic in point of code where
> object is being created using proposed special form of new statement.
>
> I hope that this proposal is reasonable.

You can approximate what you want with:

  template <typename Base, typename Derived>
  Base* create(int i)
  {
    BOOST_STATIC_ASSERT(boost::is_polymorphic<Base>::value);
    return new Derived(i);
  }

Anyway, I'd prefer to see a type trait which allows me to *check* the
condition you're interested in than a new core language extension
which fails compilation when the condition is violated.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

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





Author: maciej@maciejsobczak.com (Maciej Sobczak)
Date: Fri, 5 Sep 2003 08:30:44 +0000 (UTC)
Raw View
Bronek Kozicki wrote:
> I'd like to propose another form of "new" statement, which will can be
> used to gain guarantee that object being created has virtual destructors
> (in all its public base classes), and thus can be safely destroyed when
> used polymorphicaly.

I do not understand why you want to make it illegal to *create* such an
object. The problem is in the point of destruction, not in the point of
creation.

Consider:

class B1
{
public:
     virtual void f();
     ~B1(); // note: not virtual
};

class B2
{
public:
     virtual ~B2(); // note: virtual destructor
};

class D : public B1, public B2
{
public:
     virtual void f();
     virtual ~D();
};

Now, you want to make this (imaginable) call:

B2 *p2 = new explicit D();

illegal and have the compiler tell you that you *may* have problems
later. Why? There is no problem in this:

delete p2;

There *would* be a problem, if you do this:

B1 *p1 = dynamic_cast<B1*>(p2); // or simply = new D();
delete p1;

Considering the fact that it is only the second case that is causing
problems, what is the reason to prohibit creation of the object in the
first place and taking down the first option?


I would understand the need for compile-time checking of the *delete*
instructions. It is possible to check at compile time that:
- the pointer is to polymorphic class
- the static type of pointer is to a class that has no virtual destructor

Both of these mean (currently) that the execution od delete will reault
in undefined behavior. Redefining it to mean ill-formedness will not
break existing code (which is already broken anyway), so I think such
compile-time analysis would be useful.

Of course, there is still a second case, when the base class is not
polymorphic (it does not have any virtuals) - this case would not be
caught by the above analysis.
But hey - who publicly derives from non-polymorphic classes? :)

--
Maciej Sobczak
http://www.maciejsobczak.com/

Distributed programming lib for C, C++, Python & Tcl:
http://www.maciejsobczak.com/prog/yami/

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





Author: brok@rubikon.pl (Bronek Kozicki)
Date: Mon, 8 Sep 2003 08:26:54 +0000 (UTC)
Raw View
On Wed, 3 Sep 2003 19:17:25 +0000 (UTC), David Abrahams wrote:
>> [...] If there's any non-virtual,
>> publicly available destructor, there's risk that object being createed
>> will be destroyed using pointer, which static type will point to unsafe
>> base class, thus invoking undefined behaviour. My proposal is to make
>> such program ill-formed,
[...]
> Anyway, I'd prefer to see a type trait which allows me to *check* the
> condition you're interested in than a new core language extension
> which fails compilation when the condition is violated.

thanks for reminding type traits. I agree that it would give more
flexibility to programmers. However I think that for this purpose there
should be something else than is_polymorphic, because it's value is
"defined to be true if T is a polymorphic class (10.3)", and per 10.3
class is polymorphic, it it contains *any* virtual function. It does not
allow to verify that class has *all it's publicy available destructors
virtual* (which is main point of my proposal). Do you think, that it
would be possible to add another trait (like is_virtually_destructible)
to proposal N1424 ?


B.

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