A simpler Pimpl Idiom ?

M

mathieu

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::eek:stream & 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::eek:stream & os) const
{
os << tag << " -> " << data << std::endl;
}
private:
int tag;
std::string data;
};

void visible::print(std::eek:stream & 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
 
T

Tobias Müller

mathieu said:
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::eek:stream & 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::eek:stream & os) const
{
os << tag << " -> " << data << std::endl;
}
private:
int tag;
std::string data;
};

void visible::print(std::eek:stream & 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

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
 
R

Ryan

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
 
T

Tobias Müller

Tobias Müller said:
mathieu said:
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::eek:stream & 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::eek:stream & os) const
{
os << tag << " -> " << data << std::endl;
}
private:
int tag;
std::string data;
};

void visible::print(std::eek:stream & 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

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.

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
 
A

Alf P. Steinbach

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. :)

struct visible
{
public:
void print(std::eek:stream & 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::eek:stream & os) const
{
os << tag << " -> " << data << std::endl;
}
private:
int tag;
std::string data;
};

void visible::print(std::eek:stream & 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 ?

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
 
T

Tobias Müller

Alf P. Steinbach said:
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.
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

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. ;)
 

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,955
Messages
2,570,117
Members
46,705
Latest member
v_darius

Latest Threads

Top