4

I have this error for the following code

incomplete type ‘Foo::Pimpl’ used in nested name specifier

AnotherFoo.hpp

struct AnotherFoo {
    void methodAnotherFoo(Foo &);
};

AnotherFoo.cpp

#include "Foo.hpp"
#include "AnotherFoo.hpp"

void AnotherFoo::methodAnotherFoo(Foo &foo) {
    // here i want to save the function pointer of methodPimpl(), std::function for ex:
    std::function<void(void)> fn = std::bind(&Foo::Pimpl::methodPimpl, foo._pimpl); // <-- Here i am getting the error
}

Foo.hpp

struct Foo {
    Foo();
    class Pimpl;
    std::shared_ptr<Pimpl> _pimpl;
};

Foo.cpp

#include "Foo.hpp"

struct Foo::Pimpl {
    void methodPimpl(void) {}    
};

Foo::Foo() : _pimpl(new Pimpl) {}

main.cpp

#include "Foo.hpp"
#include "AnotherFoo.hpp"

int main() {
    Foo foo;
    AnotherFoo anotherFoo;
    anotherFoo.methodAnotherFoo(foo);
}

Does anyone have a good solution for how to fix this?

The main goal that I am trying to achieve is to keep the signature of the methodAnotherFoo method hidden from the header files.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Mihai
  • 972
  • 2
  • 13
  • 35
  • @Slava [Actually you can](https://wandbox.org/permlink/GSkDECadjY8CJ1Ah). – user7860670 Dec 12 '17 at 21:38
  • 2
    I would think you are unlikely to need a `std::shared_ptr` for a `PIMPL`, would a `std::unique_ptr` not be more appropriate? – Galik Dec 12 '17 at 21:47
  • As an alternative to the PImpl pattern, you can use an abstract base class, a factory function (perhaps as a class static function, which would return a std::unique_ptr), and an "impl" derived class that incorporates all the details. The downside is an additional indirection for the vtable lookup, but you basically have about the same cost due to pimpl. – Eljay Dec 12 '17 at 22:31

4 Answers4

4

The only file in which you may access details of Foo::Pimpl is Foo.cpp, the file in which it is defined.

You may not access it in AnotherFoo.cpp.

Your choices are:

  1. Change the implementation of AnotherFoo::methodAnotherFoo to use only the public interface of Foo.

  2. Move the implementation of AnotherFoo::methodAnotherFoo to Foo.cpp.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

If AnotherFoo.cpp needs direct access to the implementation object it is going to have to see the definition of that type, there is no way around that. Perhaps add a "detail/foo.h" header that is meant for internal use like this.

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
0

Your Pimpl implementation is not correct. It should hide details while you are trying to access them directly from methodAnotherFoo. So you should make implementation details private and provide a public proxy methods to manipulate stored implementation:

class Foo
{
    public: Foo();

    public: void method(void);

    private: class Pimpl;
    private: std::shared_ptr<Pimpl> _pimpl;
};

// Foo.cpp
struct Foo::Pimpl
{
    void methodPimpl(void) {}    
};

Foo::Foo() : _pimpl(new Pimpl) {}

void Foo::method(void) {_pimpl->method();}

And change the rest of the code to utilize those proxy methods instead of digging to implementation details:

void AnotherFoo::methodAnotherFoo(Foo &foo)
{
    std::function<void(void)> fn = std::bind(&Foo::method, foo);
}
user7860670
  • 35,849
  • 4
  • 58
  • 84
-1

One solution that i found is to move the Pimpl implementation to AnotherFoo.cpp

Mihai
  • 972
  • 2
  • 13
  • 35
  • "seems to work" is rarely (never?) good enough - unless you understand *why* and the "works" is not related to Undefined Behaviour. *Never* guess or assume in programming; *Know*. – Jesper Juhl Dec 12 '17 at 21:55