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

Discussion in 'C++' started by JackC, Aug 12, 2004.

  1. JackC

    JackC Guest

    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
     
    JackC, Aug 12, 2004
    #1
    1. Advertising

  2. In message <cfg52d$pju$1$>, JackC
    <> writes


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

    --
    Richard Herring
     
    Richard Herring, Aug 12, 2004
    #2
    1. Advertising

  3. JackC

    Ali Cehreli Guest

    On Thu, 12 Aug 2004 09:15:12 -0700, JackC wrote:

    > 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
     
    Ali Cehreli, Aug 12, 2004
    #3
  4. "JackC" <> wrote in message news:<cfg52d$pju$1$>...
    > 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.
    --
    Alan Griffiths
    http://www.octopull.demon.co.uk/
     
    Alan Griffiths, Aug 13, 2004
    #4
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Koen
    Replies:
    1
    Views:
    503
  2. Sid
    Replies:
    5
    Views:
    1,079
  3. Jason

    difference between *ptr++ and ++*ptr ?

    Jason, May 15, 2005, in forum: C Programming
    Replies:
    19
    Views:
    6,549
    Chris Torek
    May 19, 2005
  4. Matthias Kaeppler
    Replies:
    5
    Views:
    448
    Axter
    Sep 10, 2005
  5. Andrey Vul
    Replies:
    6
    Views:
    574
    James Kanze
    Oct 22, 2009
Loading...

Share This Page