1

Let's say I have a class B and a class A : public B which inherits from B. I want to expose the methods of A, which call some methods in B.

Now I want to expose these methods in the pimpl idiom - I'm really not sure how to do this:

  • Do both A and B get separate implementation classes B::impl and A::impl : public B::impl such that implementations inherit from each-other? The regular classes then don't inherit: class A and class B?

    I realized this is not possible, since the implementations are private.

  • The implementations don't subclass B::impl and A::impl but the exposed classes do class B and class A : public B. But then how are the methods in A::impl able to call the methods of the parent in B::impl?

  • Via a pointer in the arguments - see example below.

Thanks

Edit: Here is an example code snippet - is this correct?

test.hpp

#include <iostream>

class B {
private:
    class impl;
    std::unique_ptr<impl> pimpl;

public:
    B();
    ~B();
    B(B&&) = default; 
    B(const B&) = delete;
    B& operator=(B&&);
    B& operator=(const B&) = delete;

    void my_func() const;
};

class A : public B {
private:
    class impl;
    std::unique_ptr<impl> pimpl;

public:

    A();
    ~A();
    A(A&&) = default; 
    A(const A&) = delete;
    A& operator=(A&&);
    A& operator=(const A&) = delete;

    void access_my_func();
};

test.cpp

#include "test.hpp"

// Implementation of B
class B::impl
{
public:
    impl() {};

    void impl_my_func() { 
        std::cout << "impl_my_func" << std::endl;
        return; 
    };
};

// Constructor/Destructor of B
B::B() : pimpl{std::make_unique<impl>()} {};
B::~B() = default;
B& B::operator=(B&&) = default;

// Exposed method of B
void B::my_func() const {
    std::cout << "B::my_func" << std::endl;
    pimpl->impl_my_func();
    return; 
};


// Implementation of A
class A::impl
{
public:
    impl() {};

    void impl_access_my_func(const A& a_in) {
        std::cout << "impl_access_my_func" << std::endl;
        a_in.my_func();
        return;
    };
};

// Constructor/Destructor of A
A::A() : pimpl{std::make_unique<impl>()} {};
A::~A() = default;
A& A::operator=(A&&) = default;

// Exposed method of A
void A::access_my_func() {
    std::cout << "A::access_my_func" << std::endl;
    pimpl->impl_access_my_func(*this);
    return;
};




// Later in the main.cpp file
int main() {

    // Make an object
    A my_A_object;
    my_A_object.access_my_func();

    return 0;
};
smörkex
  • 281
  • 2
  • 16
  • 1
    [Consider](http://stackoverflow.com/q/825018/7571258) if pimpl really is the right idiom for your use case. Pure virtual base class might be an alternative with less syntactic overhead, especially when inheritance is involved. – zett42 Mar 25 '17 at 20:04
  • Thanks! I had not thought about this. The end goal of this project is an API, so I read that pimpl is the popular method to hide the implementation details and ensure backwards compatibility after future changes. I'm not sure if pure virtual base classes can be used for the same purpose. – smörkex Mar 25 '17 at 20:10
  • It can be used like this, see 1st example of [this question](http://stackoverflow.com/q/3092444/7571258). There can be an issue with ABI compatibility though. See comment on [this answer](http://stackoverflow.com/a/2330745/7571258). – zett42 Mar 25 '17 at 20:24

1 Answers1

1

If you subclass A from B then, A should be able to call the interface of class B. It should not have to rely on its implementation.

The Pimpl-Ideom is just a way to deal with the limitations of C++, that you cannot split the declaration of private, protected and public parts of your class. Since you would not want to expose the private parts of your class to your class users and don't want to expose the protected parts to users who don't want to subclass, the Pimpl-Ideom moves these parts away from the header file.

To answer your question:

  • class A must subclass B, that is what OOP is about.
  • class A::impl should not need to rely on B::impl to get its job done.
  • If you want to go to extremes you could envision a B::protected_impl which A::impl could subclass. For this to work you must create a separate header with the protected_impl declaration so that A::impl can use it.
Christopher Oezbek
  • 17,629
  • 3
  • 48
  • 71
  • Thanks! These points all make sense. But how then can `class A::impl` get its job done? If it needs to call a method from its parent, is the only way to pass a pointer to the object (see example `void impl_access_my_func(const A& a_in)`)? – smörkex Mar 25 '17 at 20:12