Boost logo

Boost :

Subject: [boost] [pimpl] Modularity thoughts
From: Gregory Crosswhite (gcross_at_[hidden])
Date: 2011-05-27 00:46:30


Hey everyone,

As I have stated I think that it pimpl is acceptable enough as is even
though it might contain "too much" for advanced users. Nonetheless,
having reflected on some of the other reviews I do have some thoughts on
some changes that could be made to it to make it more modular, while
still preserving a default "all-in-one" option for ordinary users.

Take the pimpl struct:

===========================

template<class Interface>
struct pimpl
{
     struct implementation;
     template<class> class value_semantics_ptr;
     template<template<class> class> class pimpl_base;
     typedef pimpl_base<value_semantics_ptr> value_semantics;
     typedef pimpl_base<boost::shared_ptr> pointer_semantics;

     static Interface null();
};

===========================

and replace it with something roughly like

===========================

template<template<class> class> equality_ops;
template<template<class> class> ordering_ops;
template<template<class> class> null_ops;
template<template<class> class> bool_ops;

template<class Interface>
struct pimpl
{
     struct implementation;
     template<class> class value_semantics_ptr;
     template<template<class> class> class pimpl_base;
     typedef pimpl_base<value_semantics_ptr> value_semantics_base;
     typedef pimpl_base<boost::shared_ptr> pointer_semantics_base;

     struct value_semantics:
         public equality_ops<value_semantics_base>,
         public ordering_ops<value_semantics_base>,
         public null_ops<value_semantics_base>,
         public bool_ops<value_semantics_base>
     {};

     struct pointer_semantics:
         public equality_ops<pointer_semantics_base>,
         public ordering_ops<pointer_semantics_base>,
         public null_ops<pointer_semantics_base>,
         public bool_ops<pointer_semantics_base>
     {};

     static Interface null();
};

===========================

Now take out all of the operations from pimpl_base and put them in
separate subclasses like so:

===========================

template<typename pimpl_base_type> struct equality_ops : public virtual
pimpl_base {
     bool operator==(equality_ops const& that) const { return (impl_ ==
that.impl_); }
     bool operator!=(equality_ops const& that) const { return !(impl_ ==
that.impl_); }
};

template<typename pimpl_base_type> struct ordering_ops : public virtual
pimpl_base {
     bool operator<(ordering_ops const& that) const { return
pimpl_base_type::impl_ < that.pimpl_base_type::impl_; }
};

template<typename pimpl_base_type> struct bool_ops : public virtual
pimpl_base {
     operator safebool_type() const { return
safebool(pimpl_base_type::impl_.get()); }
};

template<typename pimpl_base_type> struct null_ops : public virtual
pimpl_base {
     typedef typename pimpl_base_type::interface T;
     static T null() { return pimpl<T>::null(); }
};

===========================

Now the user can mix and match whatever operations he or she likes like,
or simply take the full package by default. Note that I have
intentionally left out constructors in the above classes because they
aren't needed: since we are using virtual inheritance, the user of this
library will be calling the constructor of pimpl_base directly, so we
don't even have to worry about generating lots of boilerplate to forward
the constructor arguments up to the base.

In fact, this actually makes using this library simpler in one respect:
normally a downside of using virtual inheritance in general is that
every derived class needs to call the constructors of all bases, but in
the particular case of this library this turns out to be a feature
rather than a bug! The documentation discussed how when initializing a
derived class you essentially have to call the constructor of the
immediate base class and then undo it by resetting the pointer and then
filling it with the correct type, but since we are using virtual
inheritance now we no longer need this awkward procedure since we can
just skip over the constructor of the immediate base class and call the
constructor of pimpl_base directly! This also further removes the need
for implementations to support a "null" value.

Finally, and most importantly, a strong advantage of this design is that
it is extensible so users and library writers can define their own
operation that they would like to add to classes and mix them right in.

Anyway, these are just some thoughts that I had. :-)

Cheers,
Greg


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