Topic: auto_ptr pImpl


Author: pasa@lib.hu ("Balog Pal")
Date: Mon, 4 Nov 2002 03:05:43 +0000 (UTC)
Raw View
I just read on another newsgroup, that using pImpl with auto_ptr may bite.

class Foo {
    class FooImpl;
    auto_ptr<FooImpl> pImpl;
};

for this basic case it was said the compiler is allowed to generate the Foo dtor such that the dtor call of pImpl is generated in-place as an empty thing, merely based on the incomplete type.  So the real dtor of FooImpl is not callsed on Foo destruction, and I don;t even get a warning form the compiler.

I tried to find that 'license to kill' in the standard but failed. So my question is if that is reall true, and if it is, where to find it in the standard.

Paul

---
[ 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: anthony.williamsNOSPAM@anthonyw.cjb.net (Anthony Williams)
Date: Mon, 4 Nov 2002 13:02:48 +0000 (UTC)
Raw View
pasa@lib.hu ("Balog Pal") writes:

> I just read on another newsgroup, that using pImpl with auto_ptr may bite.
>
> class Foo {
>     class FooImpl;
>     auto_ptr<FooImpl> pImpl;
> };
>
> for this basic case it was said the compiler is allowed to generate the Foo
> dtor such that the dtor call of pImpl is generated in-place as an empty
> thing, merely based on the incomplete type.  So the real dtor of FooImpl is
> not callsed on Foo destruction, and I don;t even get a warning form the
> compiler.
>
> I tried to find that 'license to kill' in the standard but failed. So my
> question is if that is reall true, and if it is, where to find it in the
> standard.

Firstly, it is undefined behaviour to instantiate a standard library template
for an incomplete type (17.4.3.6p2), so any code which requires the pImpl
member to be instantiated causes undefined behaviour.

If you get around this restriction by copy-and-pasting the definition of
std::auto_ptr to my::auto_ptr, then you can get hit by the second problem:

Secondly, if "delete x" is performed where x is a pointer to an incomplete
type, then the behaviour is undefined if x has a non-trivial destructor
(5.3.5p5). Therefore, any code which instantiates the destructor, assignment
operator, copy constructor or reset member of an auto_ptr<T> yields undefined
behaviour if T is incomplete.

This includes the default assignment operator for Foo, and all constructors
and the destructor of Foo.

As a consequence, it is generally far safer to use a smart pointer type
designed to cope with incomplete types, such as boost::shared_ptr.

Anthony
--
Anthony Williams
Senior Software Engineer, Beran Instruments Ltd.
Remove NOSPAM when replying, for timely response.

---
[ 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: pasa@lib.hu ("Balog Pal (mh)")
Date: Mon, 4 Nov 2002 17:50:44 +0000 (UTC)
Raw View
"Anthony Williams" <anthony.williamsNOSPAM@anthonyw.cjb.net> wrote in
message news:smyhlkfx.fsf@anthonyw.cjb.net...

> Firstly, it is undefined behaviour to instantiate a standard library
template
> for an incomplete type (17.4.3.6p2), so any code which requires the pImpl
> member to be instantiated causes undefined behaviour.

Good to know ;-))

However I think it is quite a serious problem with the standard library
then. Big hit on the safety of use. Was it really impossible or impractical
to define the behavior? Or at least lessen the scope?

Guess most stuff is simply ill-formed with incomplete types anyway.

Especially knowing that it's not that easy to spot all the codes where a
template instantiates, and more especially for the autogen functions.

> Secondly, if "delete x" is performed where x is a pointer to an incomplete
> type, then the behaviour is undefined if x has a non-trivial destructor
> (5.3.5p5).

Ow. That's it, thanx.

> As a consequence, it is generally far safer to use a smart pointer type
> designed to cope with incomplete types, such as boost::shared_ptr.

thanks for the pointer too, I checked the boost solution to the problem, and
incorporated it in my auto_ptr. It's just a single line:
     typedef char type_must_be_complete[sizeof(TYPE)];

in the deleter function template. Converting UB into ill-formed program, at
no real-life cost I see.
[Sure, using that the programs where you actually use an auto_ptr with an
incomplete type intetntionally, and are sure the type will have a trivial
dtor will stop compiling, is it really a case worth protecting?]

Paul

---
[ 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: alf_p_steinbach@yahoo.no.invalid (Alf P. Steinbach)
Date: Mon, 4 Nov 2002 17:54:51 +0000 (UTC)
Raw View
===================================== MODERATOR'S COMMENT:
 Please keep replies brief/on-topic/both.




===================================== END OF MODERATOR'S COMMENT
On Mon, 4 Nov 2002 13:02:48 +0000 (UTC), anthony.williamsNOSPAM@anthonyw.cjb.net
(Anthony Williams) wrote:
>
>As a consequence, it is generally far safer to use a smart pointer type
>designed to cope with incomplete types, such as boost::shared_ptr.

Uh, pardon me, but exactly how does it deal with incomplete types?

I must profess to not having ridden that particular beast.

Clueless newbie, that's me.  4 ever.


Cheers,

- Alf

---
[ 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: anthony.williamsNOSPAM@anthonyw.cjb.net (Anthony Williams)
Date: Tue, 5 Nov 2002 21:04:24 +0000 (UTC)
Raw View
alf_p_steinbach@yahoo.no.invalid (Alf P. Steinbach) writes:

> On Mon, 4 Nov 2002 13:02:48 +0000 (UTC),
> anthony.williamsNOSPAM@anthonyw.cjb.net
> (Anthony Williams) wrote:
> >
> >As a consequence, it is generally far safer to use a smart pointer type
> >designed to cope with incomplete types, such as boost::shared_ptr.
>
> Uh, pardon me, but exactly how does it deal with incomplete types?

I haven't verified that the boost implementation does exactly this, but you
can handle incomplete types in a smart pointer by:

* Don't call delete directly _anywhere_ in the pointer class --- store a
  pointer-to-function in the object which points to a function that actually
  does the delete, and call that.

* Write a function template to do the deleting, with a compile-time trap for
  the type being incomplete, such as a static assert that uses the sizeof the
  type.

* Assign the address of an appropriate instantiation of the function template
  to the function pointer in the class in the constructor that handles raw
  pointers.

This way, the only point that needs the pointed-to type to be complete is the
constructor that passes ownership into the smart pointer, which is probably
where new is called anyway. All other member functions, including the
destructor, can be safely instantiated with the pointed-to type being
incomplete. In addition, if the constructor that takes raw pointers is
instantiated where the pointed-to type is incomplete, then the static assert
will fire, and the program will fail to compile.

If you don't want the extra overhead of the function pointer, you can prevent
usage that instantiates delete on incomplete types by putting a static assert
by every delete to ensure that usage errors cause compile-time failures,
rather than undefined behaviour.

Anthony
--
Anthony Williams
Senior Software Engineer, Beran Instruments Ltd.
Remove NOSPAM when replying, for timely response.

---
[ 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                       ]