views:

73

answers:

2

This is a very noobish mistake, but I dont know whats happening here.

There are loads of pimpl examples but I dont understand why this isn't working (this was one of the examples more or less but I dont see the difference).

I have a very simple Pimpl example, but it wont work.

// Foo.hpp
#include <boost/scoped_ptr.hpp>

class Foo
{
 struct Bar;
 //boost::scoped_ptr<Bar> pImpl;
 Bar* pImpl;

public:
 Foo();
 ~Foo() {}

 int returnValue();

private:

};

and

// Foo.cpp
#include "foo.hpp"

struct Foo::Bar
{ 
 Bar() {}
 ~Bar() {}
 int value;
};

Foo::Foo() : pImpl(new Bar())
{
 pImpl->value = 7;
}

int Foo::returnValue() {
 return *pImpl->value;
}

Compiling this gives me the error. C2100: illegal indirection.

Thanks.

+6  A: 

int returnValue() should be a member function:

//  vvvvv
int Foo::returnValue() {
 return pImpl->value; // no need to dereference, value isn't a pointer
}

You need to define your constructor, copy-constructor, copy assignment operator, and destructor after the implementation class has been defined. (Otherwise the implicit destructor is dangerous, and scoped_ptr won't let you do that):

// Foo.hpp
#include <boost/scoped_ptr.hpp>

class Foo
{
    struct Bar;
    boost::scoped_ptr<Bar> pImpl;

public:
    Foo();
    ~Foo();

    int returnValue(); // could be const (so should be)

private:
    // just disable copying, like scoped_ptr
    Foo(const Foo&); // not defined
    Foo& operator=(const Foo&); // not defined
};

And:

// Foo.cpp
#include "foo.hpp"

struct Foo::Bar
{ 
    int value;
};

Foo::Foo() :
pImpl(new Bar())
{
    pImpl->value = 7;
}

Foo::~Foo()
{
    // okay, Bar defined at this point; scoped_ptr can work
}

int Foo::returnValue()
{
    return pImpl->value;
}
GMan
Thanks, that got lost when transferring the example between computers. I thought the error was different. I get illegal indirection now.
PhilCK
@Phil: Please see the other comment in my answer. `value` isn't a pointer, why are you trying to dereference it?
GMan
Ah cool thanks. Any idea when I switch the ptr to boost::scoped_ptr why I get undefined type Foo::Bar and negiative subscript error?
PhilCK
@Phil: Can you post that code? That error sounds like a static assert (a compile time error purposefully triggered because some condition wasn't met; can be done by making a negatively sized array). The thing is, you cannot use the implicit special member functions for pimpl, because the implementation class isn't defined until later. You have to do this. (Edited)
GMan
The code was above with the scoped_ptr line commented out. your code gets rid of the errors but leaves me with Foo::Foo(void) already has a body.
PhilCK
@Phil: Typo in my answer. Second one is the definition of the destructor.
GMan
As to having to define the destructor after the implementation file has been declared, there is a workaround that implemented in `shared_ptr` as to avoid having to declare and define an otherwise empty destructor. The idea is that the constructor *must* be defined after the implementation class, now, the `shared_ptr` will create at that point a `deleter` function that calls the destructor for the object. The destructor for the `shared_ptr` calls the internal `deleter` function on destruction of the last `shared_ptr`, for this it only needs the stored pointer to the `deleter`.
David Rodríguez - dribeas
... the compiler does not need to see what the implementation of the `deleter` is (and that means it does not need to know about the implementation class destructor) to implement the `shared_ptr` destructor, so it is fine if the destructor is implicitly defined when needed.
David Rodríguez - dribeas
A: 

As an aside, you may have a problem using boost::scoped_ptr for a pImpl because your pImpl is forwardly declared and you may find that the class needs to be fully visible in order to call scoped_ptr's destructor (which deletes the underlying).

Some compilers will allow you to work around this by putting the body of your destructor in the compilation unit (the .cpp file) where the class is visible.

The simplest solution is that if your destructor has to be implemented anyway you may as well just use a raw pointer and have your destructor delete it. And if you want to use something from boost to help you, derive your outer class from boost::noncopyable. Otherwise ensure you handle copy-construction and assignment properly.

You can use shared_ptr to your pImpl. You can then copy your outer class around happily although they share the same underlying unless you overload the copy-constructor and assignment operator to do otherwise.

CashCow