Objects containing a smart ptr (pimpl) and stored in STL containers - help please

J

JackC

Hi

Problem:
I wish to use a pimpl to hide implementation/storage of a class (T), but I
also want to hold objects of that class (T) in an std::vector<T> (or
similar). T is non trivial (i.e. not POD, because it has std::string and
other classes requiring proper destruction, although all drill down to POD
and strings eventually).

Classic code layout:
Header file:
class T {
public:
....
T();
~T();
private:
typedef struct Impl; //opaque, incomplete type
std::auto_ptr<Impl> pimpl; // see below
};

Source file:
struct T::Impl {
Impl() {} // called on construction
~Impl() {} // should be called on T going out of scope, see below
};
T::T : pimpl (new Impl) {}
T::~T() // this is called on a T going out of scope

auto_ptr will not do because: (a) as in classic text (Herb Sutter's
Exceptional C++) the type of Impl is incomplete, and thus the compiler is at
liberty to provide undefined behaviour for destruction of Impl (Borland's
solution is to generate (another) empty destructor for T::Impl), (b)
auto_ptr does not provide the correct copy behaviour for STL containers.

I cannot easily determine a way forward; I tried using Alan Griffith's
grin_ptr (http://www.octopull.demon.co.uk/arglib/TheGrin.html) but removing
the copy and assignment operators - this fixed problem with destruction, but
not with storing T in the STL container as do so sort operations the compile
fails. The original grin_ptr had deep and shallow cloning and I was not
sure what to do. The issue is what must be provided for success in both
areas, and is there any code out there that will do the job as I do not want
to re-invent the wheel.

NB I looked into Boost but found it hard (impossible) to work out how to
correctly install on my WinXP m/c using Borland's C++ Builder 6.0 (or for
that matter BCC5.5). Even then I cannot be sure that the smart pointers
provided are suitable.

Regards

Jack Sharman
 
R

Richard Herring

In message <[email protected]>, JackC


[classic pimpl problem]
NB I looked into Boost but found it hard (impossible) to work out how to
correctly install on my WinXP m/c using Borland's C++ Builder 6.0 (or for
that matter BCC5.5).

You don't need to "install" at all unless you need to use the parts of
Boost that require external libraries to be built - and its smart
pointers don't. Just extract the file hierarchy from the
zip/tar/whatever and add the root (e.g. boost_1_29_0) to the list of
include directories in your project. (You may find that versions later
than 1.29 don't work properly with BCC5.5 or earlier, which is why I use
that as an example ;-) .

Even then I cannot be sure that the smart pointers
provided are suitable.

boost::shared_ptr does exactly what its name suggests, and objects
containing them can be stored in STL containers without problems.

It also contains trickery to deal correctly with attempts to delete
incomplete types.

Whether you want your objects to be freely copyable with shared
ownership problems, only you can decide.
 
A

Ali Cehreli

Problem:
I wish to use a pimpl to hide implementation/storage of a class (T), but
I also want to hold objects of that class (T) in an std::vector<T> (or
similar). T is non trivial (i.e. not POD, because it has std::string
and other classes requiring proper destruction, although all drill down
to POD and strings eventually).

Classic code layout:
Header file:
class T {
public:
...
T();
~T();
private:
typedef struct Impl; //opaque, incomplete type
std::auto_ptr<Impl> pimpl; // see below
};

Source file:
struct T::Impl {
Impl() {} // called on construction
~Impl() {} // should be called on T going out of scope, see below };
T::T : pimpl (new Impl) {}
T::~T() // this is called on a T going out of scope

auto_ptr will not do because: (a) as in classic text (Herb Sutter's
Exceptional C++) the type of Impl is incomplete, and thus the compiler
is at liberty to provide undefined behaviour for destruction of Impl

This reason is not valid because you can have the destructor of your
class generated at a point where Impl is complete. The trick is to
declare the destructor of T in the header file (even if it's empty)
and define it (even if it's empty) in the implementation file.

In fact, you seem to be doing it above. I don't think there is a
problem with that.
(b) auto_ptr does not provide the correct copy behaviour for
STL containers.

This is a better reason, but not completely correct because you are
not storing any auto_ptr in any container.

The problem is with T because of not having the copy constructor and
the assignment operator that do the right thing. T is the class that
relies on auto_ptr in its implementation and it must be the class to
ensure T's suitability to containers.
is there any code out there
that will do the job as I do not want to re-invent the wheel.

Then use boost::shared_ptr.
NB I looked into Boost but found it hard (impossible) to work out how to
correctly install on my WinXP m/c using Borland's C++ Builder 6.0 (or
for that matter BCC5.5). Even then I cannot be sure that the smart
pointers provided are suitable.

The installation is as simple as copying the header files to a
directory and adding that directory to the header file search path. (I
am not sure but I think the header files don't need any make
process.)

Here is a solution using auto_ptr:

#include <memory>

class T
{
public:

T();
T(T const &);
T & operator= (T const &);
~T();

void swap(T & other);

private:
typedef struct Impl;
std::auto_ptr<Impl> pimpl;
};

struct T::Impl
{
Impl() {}
Impl(Impl const &) {}
Impl & operator= (Impl const &);
~Impl() {}
};

T::T()
:
pimpl (new Impl())
{}

T::T(T const & other)
:
pimpl(new Impl(*(other.pimpl)))
{}

void T::swap(T & other)
{
std::swap(pimpl, other.pimpl);
}

T & T::eek:perator= (T const & other)
{
T temp(other);
this->swap(temp);
return *this;
}

// There is NO PROBLEM here, because Impl is completely defined when
// auto_ptr::~auto_ptr is used
T::~T()
{}

#include <vector>

int main()
{
std::vector<T> Ts;
Ts.push_back(T());
Ts.push_back(T());
}

Ali
 
A

Alan Griffiths

JackC said:
Hi

Problem:
I wish to use a pimpl to hide implementation/storage of a class (T), but I ....
I cannot easily determine a way forward; I tried using Alan Griffith's
grin_ptr (http://www.octopull.demon.co.uk/arglib/TheGrin.html) but removing
the copy and assignment operators - this fixed problem with destruction, but
not with storing T in the STL container as do so sort operations the compile
fails.

Try not removing the copy and assignment operators!!
The original grin_ptr had deep and shallow cloning and I was not
sure what to do. The issue is what must be provided for success in both
areas, and is there any code out there that will do the job as I do not want
to re-invent the wheel.

As you are not deriving from T::Impl then you don't need to do
anything, the default behaviour is what you want.
NB I looked into Boost but found it hard (impossible) to work out how to
correctly install on my WinXP m/c using Borland's C++ Builder 6.0 (or for
that matter BCC5.5).

For most of Boost you just expand the archive and add it to the
include path. (There are a few libraries within you may need to
build, but not the smart pointers.)
Even then I cannot be sure that the smart pointers
provided are suitable.

boost::shared_ptr provides shared ownership semantics - so, unless you
want copies of T to share implementation, you must provide the copy
semantics for yourself.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top