A simpler Pimpl Idiom ?

Discussion in 'C++' started by mathieu, Oct 21, 2013.

  1. mathieu

    mathieu Guest

    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
    mathieu, Oct 21, 2013
    #1
    1. Advertising

  2. 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>
    >
    > 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
    Tobias Müller, Oct 21, 2013
    #2
    1. Advertising

  3. mathieu

    Ryan Guest

    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
    Ryan, Oct 21, 2013
    #3
  4. Tobias Müller <> wrote:
    > 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>
    >>
    >> 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
    Tobias Müller, Oct 21, 2013
    #4
  5. 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. :)


    > 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
    Alf P. Steinbach, Oct 21, 2013
    #5
  6. "Alf P. Steinbach" <> 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
    Tobias Müller, Oct 21, 2013
    #6
  7. mathieu

    Öö Tiib Guest

    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. ;)
    Öö Tiib, Oct 21, 2013
    #7
    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. Icosahedron

    Pimpl Idiom

    Icosahedron, Nov 20, 2003, in forum: C++
    Replies:
    7
    Views:
    672
    Icosahedron
    Nov 22, 2003
  2. Debajit  Adhikary
    Replies:
    2
    Views:
    2,012
    Christopher Benson-Manica
    Jul 15, 2004
  3. Peteris Krumins
    Replies:
    2
    Views:
    451
    Peteris Krumins
    Aug 31, 2005
  4. jimmy

    friendship and pImpl idiom

    jimmy, Feb 8, 2006, in forum: C++
    Replies:
    3
    Views:
    424
    Alf P. Steinbach
    Feb 9, 2006
  5. Noah Roberts

    pimpl idiom and singletons

    Noah Roberts, May 24, 2007, in forum: C++
    Replies:
    4
    Views:
    424
    Noah Roberts
    May 25, 2007
Loading...

Share This Page