Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

A simpler Pimpl Idiom ?

69 views
Skip to first unread message

mathieu

unread,
Oct 21, 2013, 9:45:01 AM10/21/13
to
I have been reading "The Joy of Pimpls " [1], but this seems over-complicated IMHO. It requires a "back-pointer" just to split object in halves.

I am thinking of providing something similar (at least in my scenario), where I can hide the implementation completely.

I am thinking in something like this:

$ cat demo.cxx
// public stuff:
#include <iosfwd>

struct visible
{
public:
void print(std::ostream & os) const;
protected:
visible(){}
private:
visible(const visible &);
visible & operator = (const visible &);
};

visible * get_one();

// implementation details:
#include <iostream>

struct not_visible : public visible
{
public:
not_visible(int t, const char *v):tag(t),data(v){}
void print_impl(std::ostream & os) const
{
os << tag << " -> " << data << std::endl;
}
private:
int tag;
std::string data;
};

void visible::print(std::ostream & os) const
{
static_cast<const not_visible*>(this)->print_impl(os);
}

visible * get_one()
{
// dummy implementation just for the demo:
static not_visible v(123,"hello");
visible *ptr = &v;
return ptr;
}

int main()
{
visible *u = get_one();
u->print(std::cout);
return 0;
}


Does anyone sees anything wrong with this implementation ? How would one mark the class 'visible' an interface-only (it should not be derived in user applications).

Thanks,
-Mathieu

[1] http://www.gotw.ca/publications/mill05.htm

Tobias Müller

unread,
Oct 21, 2013, 12:19:27 PM10/21/13
to
IMO, you gain nothing, while there are some major disadvantages.

You gain nothing, because the client code always has to operate on
pointers. In both cases you have one indirection.

The disadvantages are:
- You cannot use 'visible' objects as values, only pointers. With pimpl you
can.
- You cannot use constructors and destructors in client code, but have to
use (factory) functions instead.

If I had to deal with such an interface I would wrap it immediately in a
class, just for those reasons. Especially for having a destructor.

Tobi

Ryan

unread,
Oct 21, 2013, 12:24:01 PM10/21/13
to
On Monday, October 21, 2013 6:45:01 AM UTC-7, mathieu wrote:
> I have been reading "The Joy of Pimpls " [1], but this seems over-complicated IMHO. It requires a "back-pointer" just to split object in halves.
>
> I am thinking of providing something similar (at least in my scenario), where I can hide the implementation completely.

You might want to look at the following code to see if it works for you. Might save you the time of having to fully develop your own code to do this.

Documentation
http://www.drdobbs.com/cpp/making-pimpl-easy/205918714

Source Code
https://github.com/boost-vault/Miscellaneous/blob/master/Pimpl.zip

Ryan

Tobias Müller

unread,
Oct 21, 2013, 12:45:44 PM10/21/13
to
Oh and even worse, there can only exist one object at a time since you are
using a static object. It is essentially a singleton.

Tobi

Alf P. Steinbach

unread,
Oct 21, 2013, 2:23:53 PM10/21/13
to
On 21.10.2013 15:45, mathieu wrote:
> I have been reading "The Joy of Pimpls " [1], but this seems over-complicated
> IMHO. It requires a "back-pointer" just to split object in halves.
>
> I am thinking of providing something similar (at least in my scenario), where I
> can hide the implementation completely.
>
> I am thinking in something like this:
>
> $ cat demo.cxx
> // public stuff:
> #include <iosfwd>

Good practice. :-)
Every variant has its own advantages and drawbacks.

Wit the common PIMPL idiom client code can inherit the implementation of
the exposed class, i.e. client code can inherit a concrete, instantiable
class. This is lost with the abstract class idea (above). But one avoids
some indirection and dynamic allocation.

Not sure if Herb mentions this, but the main purpose of a PIMPL is to
encapsulate use of dirty or large or platform-specific (whatever)
headers. If that's not a problem, then PIMPL is usually not needed. A
main cost of PIMPL is that it requires separate compilation.


> How would one mark the class 'visible' an interface-only (it should
> not be derived in user applications).

For C++11, <url: http://en.cppreference.com/w/cpp/language/final>, using
the keyword "final".

For C++03, <url: http://www.parashift.com/c++-faq/final-classes.html>,
using a virtual base class with limited accessibility (because a virtual
base class must be constructed in the most derived class).

But consider whether there is any concrete advantage in doing that: it's
often a good idea to make the intended usage natural and easy and the
hazardous usage less easy, but (well OK it's famous last words) what
could possibly go wrong?


Cheers & hth.,

- Alf

Tobias Müller

unread,
Oct 21, 2013, 3:05:39 PM10/21/13
to
"Alf P. Steinbach" <alf.p.stein...@gmail.com> wrote:
[...]
> Not sure if Herb mentions this, but the main purpose of a PIMPL is to
> encapsulate use of dirty or large or platform-specific (whatever)
> headers. If that's not a problem, then PIMPL is usually not needed. A
> main cost of PIMPL is that it requires separate compilation.

Separate compilation may be the main cost, but it's also one of the main
use case.
For software that comes only as binary + header it is convenient way for
hiding the implementation and maintaining binary compatibility.

>> How would one mark the class 'visible' an interface-only (it should
>> not be derived in user applications).
>
> For C++11, <url: http://en.cppreference.com/w/cpp/language/final>, using
> the keyword "final".

But then it is no more possible to derive 'invisible' from it neither. Or
am I missing something?

[...]

Tobi

Öö Tiib

unread,
Oct 21, 2013, 4:07:43 PM10/21/13
to
On Monday, 21 October 2013 16:45:01 UTC+3, mathieu wrote:
> I have been reading "The Joy of Pimpls " [1], but this
> seems over-complicated IMHO. It requires a "back-pointer" just to
> split object in halves.

Additional indirection by that "back-pointer" is not as expensive as
lot of people seem to think, also it provides cheap swaps/moves for
free. So classical Pimpl is on lot of cases best thing.

If moving only private member functions to compilation-unit local
is the goal then that can be done without any pointers. I posted one way
how in March to comp.lang.c++.moderated:
header https://groups.google.com/d/msg/comp.lang.c++.moderated/GJyXgeEtAOA/PwXSG_ZGeGgJ

If you want to hide member data too AND want to get rid of that
pointer then in C++11 you can just have sufficient buffer for private
implementation in class. Verify that it is sufficient in compilation-unit
using C++11 'static_assert', 'alignas', 'alignof' and 'sizeof'.
Then you have to use placement new, explicit destructor calls and
reinterpret_cast to manage private implementation in that buffer.
Without C++11 one can use compiler extensions to achieve same (lot of
stuff in Boost have done it for decade).

IOW your implementation has numerous limitations that are not needed and
can be easily avoided. ;)
0 new messages