Zythos
Zythos

Reputation: 180

PIMPL idiom for a pointer to a class in C++

I have a working interface for two programs (ProgramA and ProgramB) that I would like to improve decoupling both programs as much as possible. The case that I want to cover is making a call from ProgramA to a class from ProgramB (Compute_Prop) that can only be initialized with some arguments which I do not now in advance. Hence, I use a pointer in the header. Currently, I have something like this:

interface.h

#include "programB.h" // loads Compute_Prop

class Compute {
  public:
    Compute();
    Compute(targ1 arg1, targ2 arg2);
    ~Compute();
    // some methods ...
  private:
    Compute_Prop* compute;
};

interface.cpp

#include "programB.h"
#include "interface.h"

#include "programA.h"

Compute::Compute() = default;

Compute::~Compute() {                                                                                                     
    delete compute;                                                                                                                                                                                                                                                                                                                                                                   
}

Compute::Compute(arg1, arg2) {

  // do something ... to get data

  compute = new Compute_Prop( &data, arg2 );
}

Then, I try to imitate the PIMPL idiom with the following

interface.h

#include "programB.h" // loads Compute_Prop

class Compute {
  public:
    Compute();
    Compute(targ1 arg1, targ2 arg2);
    ~Compute();
    // some methods ...
  private:
    class PIMPL;
    PIMPL* compute;
};

interface.cpp

#include "programB.h"
#include "interface.h"

#include "programA.h"

Compute::PIMPL = Compute_Prop;

Compute::Compute() = default;

Compute::~Compute() {                                                                             
    delete compute;                                                                                                                                                                     
}

Compute::Compute(arg1, arg2) {

  // do something ... to get data

  compute = new Compute_Prop( &data, arg2 );
}

but the compiler says:

error: expected unqualified-id
  Compute::PIMPL = Compute_Prop;
                 ^

I guess that it has something to do with Compute_Prop not having an empty constructor. I can't come up with something that works. What should I do? Something like a pointer to a pointer, maybe? As a restriction, I cannot modify programB.

Note: As it is probably already clear from above, my understanding of low level C++/C is scarce.

EDIT: I introduced the corrections suggested by @n.m. and @Matthieu Brucher

Upvotes: 1

Views: 875

Answers (2)

Zythos
Zythos

Reputation: 180

I may have come across a simple solution. I post it here so you can judge if it is adequate, or even if it can be improved --- sure. I am convinced that runtime polymorphism is not needed, not even polymorphism. The member variable compute is going to be a pointer to a Compute_Prop type anyway. Then, given that performance is critical here: why running the extra overhead of virtual member functions?

The point here is to reach an implementation that hides the inclusion of Compute_Prop without loosing performance. How? This particular solution uses a templated class and then explicit instantiation. The point is that instantiation can be done in the implementation. Got it from a Fluent C++ blog post. Also, this post has hints for how the implementation should be done. A prototype would be:

interface.h

template <typename T>
class Compute {
  public:
    Compute();
    Compute(targ1 arg1, targ2 arg2);
    ~Compute();
    // some methods ...
  private:
    T* compute; // No need to state that is going to be T:=Compute_Prop
};

interface_impl.h

#include "interface.h"    
#include "programA.h"

template <typename T>
Compute::Compute() = default;

template <typename T>
Compute::~Compute() {                                                                                                              
    delete compute;                                                                                                                                                                                                                                                                                                                                                                
}

template <typename T>
Compute::Compute(arg1, arg2) {

  // do something ... to get data

  compute = new T( &data, arg2 );
}

interface.cpp

 #include "interface.h"
 #include "interface_impl.h"
 #include "programA.h"
 #include "programB.h" // loads Compute_Prop

 int main(int argc, char** argv) {

   template class Compute<Compute_Prop>;

 }

Another related question that might be useful for those with the same dilemma.

Upvotes: 0

Poeta Kodu
Poeta Kodu

Reputation: 1150

Your implementation should use an interface (or in fact a class with only abstract methods) as a base class. You cannot assign types in C++. You can only create typedefs and aliases, like that:

using PIMPLType = Compute_Prop;

However this won't work in your case. This is how it should be implemented (also with possibility of multiple implementations):

class IImplementation
{
public:
    virtual void saySomething() = 0;
};

class ImplementationA : public IImplementation
{
public:
    virtual void saySomething() override {
        std::cout << "A";
    }
};
class ImplementationB : public IImplementation
{
public:
    virtual void saySomething() override {
        std::cout << "B";
    }
};

class Foo {
    IImplementation *pimpl;
public:
    Foo()
        : pimpl(new ImplementationA)
    {}

    ~Foo() { delete pimpl; }

    void saySomething() {
         pimpl->saySomething();
    }
};

Upvotes: 1

Related Questions