Boost logo

Boost :

From: Dietmar Kuehl (dietmar.kuehl_at_[hidden])
Date: 2000-01-17 13:05:59


Hi,

I'm just hacking on the improvements to the directory iterator and one
of the things I want to do is to be a good Booster. That is, I want to
replace my own [intrusive] reference counting with a suitable smart
pointer from the Boost library. I think 'boost::shared_ptr' is the right
choice but as it turns out it does not really match the use I have...

Internally to my directory iterator, I'm using a pointer to the actual
representation. This is done for two reasons:

- To share the representation between multiple copies of the directory
  iterator very much like 'std::istream_iterators' share a common stream,
  that is, if one of the copies of the directory iterator is advanced, all
  others are advanced, too.

- To move system dependencies out of the header and only include
  system specific headers in the implementation (well, I'm not yet sure
  that I can do this but that is a different discussion), ie. the stuff
  implements the "pimpl idiom".

Unfortunately, this does not work smoothly: I need to define the
class to be used as template argument to the 'boost::shared_ptr' in the
header to make it work. One option would be something like this:

  // directory.hpp:
  struct representation { virtual ~representation(); };
  class dir_it {
    // ...
  private:
    boost::shared_ptr<representation> m_rep;
  };

... and use a class derived from 'representation' in the implementation.
However, this is rather inconvenient since I would have to cast the
pointer whenever I want to access it, ie.

  static_cast<real_representation*>(m_rep)->m_member ...

Now, what are the options to avoid this? I don't think that there is any
using the type 'boost::shared_ptr' but I can imagine another class, call
it 'boost::pimpl_ptr' for now, which might address this problem. This
class would first of all provide a base class like the one mentioned
above which is used to maintain the pointer to the otherwise unknown
class. Thus, there is a restriction on the template argument that it has
to be a class derived from this base class. However, it is not necessary
to say so except when actually using the corresponding members in
some translation unit. Here is a basic outline of the important members
of the class 'pimpl_ptr':

  struct base { virtual ~base() {} };
  template <class T> class pimpl_ptr {
  public:
    // copy ctor, copy assignment, etc. are just not shown
    pimpl_ptr(T* ptr): m_ptr(ptr) {}
    ~pimpl_ptr() { delete m_ptr; }
    T* operator->() { return static_cast<T*>(m_ptr; }
  private:
    base* m_ptr;
  };

Now, copying, assigning, and destroying objects of this class in a
translation unit not aware of a definition of some imcomplete type used
as template parameter would just befine. Of course, the constructor
taking a pointer to this type and the operator->() can't be used without
a definition of this type. ... but this is exactly what is desired in the
pimpl idiom anyway.

There is one restriction on types used with this class, namely that they
need to be derived from 'base' (the name is probably subject to change).
I have no problem with this restriction if I can avoid all those casts in
return...

Is this
- discussed before and I just didn't paid attention?
- plain stupid (not that unlikely)?
- a good idea?
- an interesting aspect but not really relevant to justify a new class?

Thanks for any input,
  dk


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk