0

While trying to remove all implementation details from a header file I decide to use and try out PIMPL idiom. The majority if not all examples, e.g. cppreference, I've seen use levels of indirection which reason I can't understand besides encapsulation. The examples always come with this flavor pattern.

Typical

//A.hpp
class A
{
public:
    A(int);
    //More non-static class methods
    void set(int);

private:
    struct a_impl;
    std::unique_ptr<struct a_impl> pimpl;
};

//A.cpp
struct A::a_impl
{
private:
    int i;
public:
    a_impl(int i) : i{i}{}
    //More non-static implementation class methods
    void set(int i) {this->i = i;}
};
A::A(int i) : std::make_unique<struct a_impl>(i) {}
A::set(int i) {pimpl->set(i);}  //????
A:://More indirect calls to non-static member functions through pointer

At certain point I start wondering why do I need all this level of complexity and indirection if we are talking about implementation. Why not something simplest without all those indirect calls.

Why not ?

    //A.hpp
class A
{
public:
    A(int);
    //.....
    void set(int);

private:
    struct a_impl;
    std::unique_ptr<struct a_impl> pimpl;
};

//A.cpp
struct A::a_impl
{
    int i;
}

A::A(int i) : std::make_unique<struct a_impl>() { pimpl->i = i; }
A::set(int i) { pimpl->i = i; } //!!!!

What I would like to clarify:

1-Are all this samples presented this way just for a matter of education and good encapsulation practices?

2-Is there any other good reason I'm missing to add this level of complexity and overhead besides question 1?

3-Or what I put out as an alternative isn't PIMPL idiom?

Jacinto Resende
  • 343
  • 2
  • 13
  • `struct A` could be much more complicated and direct calls can break some logic inside – snake_style Dec 20 '18 at 06:43
  • @snake_style Indeed. Each case is one case. With my question I'm not suggesting that there aren't cases where this level of indirection is not only required but almost mandatory (e.g. class invariants). What start to puzzle me was the fact that all examples were presented in the same way no matter how simple they were. That trigger doubts in my mind. – Jacinto Resende Dec 20 '18 at 06:53
  • @JacintoResende The purpose of pimpl idiom is to provide a stable ABI, so that you can change the data member without breaking binary compatibility. As long as all data is in a pointer, other stuff doesn't matter, and how to actual implement is *entirely* opinion based. – llllllllll Dec 20 '18 at 06:58
  • This method may be somewhat related, may be of interest: https://stackoverflow.com/questions/24635255/is-it-possible-to-write-an-agile-pimpl-in-c/24638702#24638702 – Galik Dec 20 '18 at 08:08

1 Answers1

4

Your own approach and the one you compare it against are just two different flavors of the idiom. Herb Sutter listed them, and more, in GotW #100:

What parts of the class should go into the impl object? Some potential options include:

  • put all private data (but not functions) into impl;
  • put all private members into impl;
  • put all private and protected members into impl;
  • put all private nonvirtual members into impl;
  • put everything into impl, and write the public class itself as only the public interface, each implemented as a simple forwarding function (a handle/body variant).

Each has its own strong and weak points. For the case you study, your approach does seem better. For other cases, a pimpl with behavior and not just state may be more appropriate. There isn't really a one size fits all.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458