Pimpl not working - c++

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.

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;
}

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.

Related

Constructor and destructor in c++ when using the pimpl idiom

I come from Java that has a different way in handling what's private and has to be hided regarding a class implementation and it also has a garbage collector which means there is no need for a destructor.
I learned the basics of how to implement a class in c++ but I need to better understand how to implement a class, in particular the constructor and destructor, when using the pimpl idiom.
hpp file:
class MyClass{
public:
MyClass();
MyClass(std::vector<int>& arr);
~MyClass();
private:
struct Impl;
Impl* pimpl;
};
cpp file:
#include "MyClass.hpp"
using namespace std;
struct MyClass::Impl{
vector<int> arr;
int var;
};
I wrote the sample code of a class I need to work on(which means I can't change the way the pimpl idiom is used) using the pimpl idiom and I'm looking for an answer to these questions:
How do I implement the constructor to create an empty instance of MyClass?
MyClass::MyClass(){}
How do I implement the constructor to create an instance of MyClass with these arguments?
MyClass::MyClass(vector<int>& arr,int var){}
How do I implement the destructor?
MyClass::~MyClass(){}
EDIT:
How I would do it:
MyClass::MyClass(): pimpl(new Impl) {}
MyClass::MyClass(vector<int>& arr,int var): pimpl(new Impl) {
pimpl->arr=arr;
pimpl->var=var;
}
MyClass::~MyClass(){
delete pimpl;
}
This is an example of a correct way to implement PIMPL idiom in modern C++:
foo.hpp
#pragma once
#include <memory>
class Foo {
public:
Foo();
~Foo();
Foo(Foo const &) = delete;
Foo &operator=(Foo const &) = delete;
Foo(Foo &&) noexcept;
Foo &operator=(Foo &&) noexcept;
void bar();
private:
class impl;
std::unique_ptr<impl> pimpl_;
};
foo.cpp:
#include "foo.hpp"
#include <iostream>
class Foo::impl {
public:
impl() = default;
~impl() = default;
impl(impl const &) = default;
impl &operator=(impl const &) = default;
impl(impl &&) noexcept = default;
impl &operator=(impl &&) noexcept = default;
void bar() { std::cout << "bar" << std::endl; }
};
Foo::Foo() : pimpl_(new impl{}) {}
Foo::~Foo() = default;
Foo::Foo(Foo &&) noexcept = default;
Foo &Foo::operator=(Foo &&) noexcept = default;
void Foo::bar() { pimpl_->bar(); }
main.cpp:
#include "foo.hpp"
int main(int argc, char const *argv[]) {
Foo foo;
foo.bar();
return 0;
}
There are a few words that are to be said:
use a smart pointer (unique or shared) to hold reference(s) to 'impl' object. It'll help you like in JAVA control number of references to an underlying object. In this example, as just Foo class is gone 'impl' is automatically destroyed, there is no need to manually watch the lifetime of the 'impl' object
'impl' MUST be always completely declared and defined in *.cpp file (or bunch of files) or internal *.hpp and/or *.cpp files so that a user of your front class (i.e., in this example 'Foo') that aggregates 'impl' is impossible to see any changes and content of your 'impl' class, that is why PIMPL idiom exists: to persist the interface of the front class for a user and all the changes mostly shall be done in the hidden 'impl' class
The destructor of the front class MUST be always defined in *.cpp file since the compiler must know how to destroy the 'impl' that is not seen thoroughly from the *.hpp file where a front class is declared
The special functions with rvalue-references (move-ctor and move-assignment operator) MUST also be defined in *.cpp file due to the same reason as in the previous clause

Forward declaration with reference-counting smart pointer

I have implemented a class reference<T> that keeps track of the amount of references to a T which derives from reference_countable.
I have a problem with it with respect to forward declaring the T of the reference<T>, similar to that of std::unique_ptr<T>. With std::unique_ptr<T> the issue comes from the destructor not beeing known, so all you have to do is put the destructor of your class into the cpp file, like so:
header:
class MyClass;
class A
{
std::unique_ptr<MyClass> my_class;
}
implementation:
A:~A() = default;
However, in my version, where std::unique_ptr<MyClass> is replaced by reference<MyClass>, the reference_countable must also be decremented and incremented. Which requires me to also put the copy-assignment and copy-constructor of A in cpp file when MyClass is only forward declared.
Is there a way to avoid having to put the implementation for these three functions in the cpp file for all classes with a reference<T> member?
I've tried to describe it as simple as I can, but for more detail, here is a simple version of the problem. Specifically, the need to define the copy-constructor of A in a.cpp.
my_class.h
#pragma once
#include "minimal_ref_counter.h"
class MyClass : public minimal_reference_countable
{
};
a.h
#pragma once
#include "minimal_ref_counter.h"
class MyClass;
class A
{
public:
A();
~A();
A(const A&);
minimal_reference_counter<MyClass> my_class;
};
a.cpp
#include "test.h"
#include "myclass.h"
A::A()
: my_class(new MyClass())
{}
A::~A() = default;
A::A(const A&) = default;
some_other_code.cpp
#include "a.h"
void some_function()
{
A a1;
A a2 = a1; // this code does not compile without the copy assignment operator beeing implemented externally.
}
minimal_ref_counter.h
#pragma once
#include <atomic>
class minimal_reference_countable
{
template<typename T>
friend class minimal_reference_counter;
std::atomic_int m_references = 0;
auto reference_count() const { return m_references.load(); }
void decrement() { --m_references; }
void increment() { ++m_references; }
};
template<typename T>
class minimal_reference_counter
{
public:
minimal_reference_counter(T* t = nullptr)
{
assign(t);
}
~minimal_reference_counter()
{
reset();
}
minimal_reference_counter(const minimal_reference_counter& r)
{
*this = r;
}
minimal_reference_counter(minimal_reference_counter&& r)
{
*this = std::move(r);
}
minimal_reference_counter& operator=(const minimal_reference_counter& r)
{
assign(r.m_ptr);
return *this;
}
minimal_reference_counter& operator=(minimal_reference_counter&& r)
{
assign(r.m_ptr);
r.reset();
return *this;
}
void reset()
{
if (!m_ptr) return;
m_ptr->decrement();
if (m_ptr->reference_count() == 0)
{
delete m_ptr;
}
m_ptr = nullptr;
}
private:
void assign(T* ptr)
{
reset();
m_ptr = ptr;
if (m_ptr) m_ptr->increment();
}
T* m_ptr = nullptr;
};
C++ templates are lazy. Instantiations are deferred until necessary. The reason to put the destructor in the cpp file when you have a member std::unique_ptr of an incomplete type, later completed, is that the destructor needs the destructor of unique_ptr, thus instantiating it, which needs the destructor of the pointed-to type which requires that type to be complete. This chain of dependencies triggered by the definition of the destructor must be deferred until such time as the pointed-to type is complete.
In your reference counted pointer case, you wonder if the copy constructor, copy assignment operator, and destructor of the pointer necessarily require completeness of the pointed-to type, and likewise their instantiations be deferred until such time as the pointed to type is complete.
Because the pointer destructor potentially invokes the destructor of the pointed-to type, that type necessarily must be complete when the pointer destructor is instantiated. This is similar to unique_ptr and so should be unsurprising.
Similarly, the pointer copy assignment potentially invokes the destructor of the pointed-to type. It replaces the pointee with another pointee and decrements the original pointee's reference count and destroys it if necessary. So, for the same reason as the destructor, the pointer copy assignment operator requires the pointed-to type necessarily be complete when it is instantiated.
The copy constructor is more subtle. No potential destructions of the pointed-to type are invoked. However, with this implementation, we act on a base class of the pointed-to type to increment the reference count. This requires completeness, as otherwise there are no base classes. So, with this implementation, yes, the poitned-to type necessarily must be complete when the copy constructor is instantiated.
There are alternative implementations, of course. One could keep both a T* and a minimal_reference_counter<T>*, instead of finding the latter from the former as a base class. shared_ptr does this. In fact, one could also store the destructor as a function pointer and not require the other awkward instantiation deferrals. shared_ptr does this too.

Storing a class that uses the PIMPL idiom in a std::vector

I am writing an application that needs to store objects of a class that uses the PIMPL idiom in a std::vector. Because the class uses std::unique_ptr to store a pointer to it's implementation and std::unique_ptr is not copyable, the class itself is not copyable. std::vector should still work in this case because the class is still movable.
To avoid creating a copy I tried using emplace_back to construct the elements directly into the vector, but for some reason it still complains that it is trying to call the copy constructor!
I've written a simple example to demonstrate the problem.
test.h:
#pragma once
#include <memory>
// Simple test class implemented using the PIMPL (pointer to implementation) idiom
class Test
{
public:
Test(const int value);
~Test();
void DoSomething();
private:
// Forward declare implementation struct
struct Impl;
// Pointer to the implementation
std::unique_ptr<Impl> m_impl;
};
test.cpp
#include "test.h"
#include <iostream>
// Implementation struct definition
struct Test::Impl
{
Impl(const int value)
: m_value(value)
{}
void DoSomething()
{
std::cout << "value = " << m_value << std::endl;
}
private:
int m_value;
};
// Construct the class and create an instance of the implementation struct
Test::Test(const int value)
: m_impl(std::make_unique<Impl>(value))
{}
Test::~Test() = default;
// Forward function calls to the implementation struct
void Test::DoSomething()
{
m_impl->DoSomething();
}
main.cpp:
#include "test.h"
#include <vector>
int main()
{
std::vector<Test> v;
// Even though I'm using "emplace_back" it still seems to be invoking the copy constructor!
v.emplace_back(42);
return 0;
}
When I try to compile this code I get the following error:
error C2280: 'Test::Test(const Test &)': attempting to reference a deleted function
This leads to two questions...
Why is it attempting to use the copy constructor even though I explicitly used emplace_back?
How can I get this to compile without errors? The object is movable so according to the standard it should be able to be stored in a std::vector.
Add Test(Test&&) noexcept; and Test& operator=(Test&&) noexcept; then =default them in your cpp file.
You should probably make Test(const int value) explicit while you are at it.
In modern gcc/clang at least, you can =default the move ctor in the header. You cannot do this to operator= because it can delete the left hand side pointer, nor the zero argument constructor (something to do with constructing the default deleter?) or destructor (which has to call the default deleter).

Hiding implementation of members owned by PImpl-objects

I have a class which I want to create an interface for without showing any of the implementation (not because it's closed source, but because of a lot of unnecessary headers such as OpenGL coming with it), let's call that class Foo. I know this is commonly done through either the PImpl-idiom or virtual interfaces, and I've implemented the PImpl-idiom for that class.
However, this class has public functions which returns other classes that are also including those headers, so obviously I can't return those objects as-is as that'd require me to include headers I don't want to include in Foo.h. However, these classes are used internally within the library a lot, and thus I don't want to implement them with PImpl as that'd introduce a lot of overhead (they're used a lot within a 3D renderer's code). Let's call one of them Bar:
Foo.h:
#include "Bar.h"
class Foo
{
private:
class impl;
std::unique_ptr<impl> m_pimpl;
public:
Foo();
Bar& get_bar();
};
Foo.cpp:
#include "Foo.h"
class Foo::impl {
private:
Bar m_bar;
public:
Bar& get_bar();
};
Foo::Foo() : m_pimpl{std::make_unique<impl>()}
{ }
Bar& Foo::get_bar() {
return m_pimpl->get_bar();
}
For this to work I need to #include "Bar.h", but Bar.h might include headers I want to hide. This leaves me with the option to make Bar use the PImpl-idiom as well, but I don't want that overhead for Bar because Bar is used a lot internally within Foo. However, I figured a way to solve this, but I'm not very sure about it as I haven't seen it used anywhere before:
Using PImpl without an owning pointer/reference to simply wrap a class and create a interface for it. This way the overhead only applies when outside of Foo, but internally it'll still use the non-wrapped class.
For example, let's say PBar is wrapping Bar:
PBar.h:
class Bar;
class PBar {
private:
Bar &m_bar;
public:
explicit PBar(Bar &bar);
void do_stuff();
};
PBar.cpp:
#include "PBar.h"
#include "../impl/Bar.h" // This is the actual Bar implementation
PBar::PBar(Bar &bar) : m_bar(bar) {
}
void PBar::do_stuff() {
m_bar.do_stuff();
}
And Foo instantiates PBar on creation with a reference to the actual Bar inside the object:
Foo.h
#include "PBar.h"
class Foo
{
private:
class impl;
std::unique_ptr<impl> m_pimpl;
PBar m_bar;
public:
Foo();
PBar& get_bar() { return m_bar; }
};
Foo.cpp:
class Foo::impl {
private:
Bar m_bar;
public:
Bar& get_bar();
};
Foo::Foo() : m_pimpl{std::make_unique<impl>()},
m_bar(impl->get_bar())
{ }
Is this pattern ever used? Are there any other ways I can solve this problem? It's more or less the same principle as PImpl, but is there anything bad about it I haven't yet thought about? It definitely feels even less clean, but I can't see how this could be done in any other way.
Also, I want neither PBar or Bar to be constructable outside of Foo, so that's not a problem.
Thanks!
You cannot (should not) change the object referenced by a reference member: how do you do here: Foo a,b; a=b; (supposing you initialize a non null unique_ptr). This is easily corrected replacing the reference by a pointer.
This look like a good idea, what you do is caching a dereferencement. But you are loosing some efficiency of the pimpl idiom and you are doubling the size of Foo.
Have you thought in making the class impl standard layout and putting Bar at a known offset inside impl:
Foo.h
constexpr auto impl_bar_offset = 8;
//...
class Foo{
private:
class impl;
std::unique_ptr<impl> m_impl;
public:
bar& get_bar(){
assert(m_impl);
return *reinterpret_cast<bar*>(
reinterpret_cast<unsigned char*>(m_impl.get())+impl_bar_offset);
}
};
Foo.cpp
class impl{
long a_long;
bar a_bar;
//...
};
static_assert(impl_bar_offset==offsetof(impl,a_bar));

unique_ptr pimpl and incomplete types

This is not a dupe of std::unique_ptr with an incomplete type won't compile.
Consider the code below:
#include <memory>
struct X
{
X();
~X();
struct Impl;
std::unique_ptr<Impl> up_;
};
struct Impl {}; // fully visible here
X::X() : up_{nullptr}{}
X::~X() = default;
int main()
{
X x;
}
Live on Coliru
gcc/clang both spit an error saying that Impl is incomplete. However, I provide a default destructor for X after Impl is fully visible, so IMO the code should compile. Why doesn't? Now comes the surprise: If I make Impl an inner class, i.e. define
struct X::Impl{};
instead, then the code compiles, even without providing a destructor. Why is this happening? Shouldn't we provide such a default destructor, at least according to the link mentioned in the first line?
You have two different structs named Impl.
struct X
{
struct Impl; // Here you declare X::Impl
std::unique_ptr<Impl> up_; // Here you create a pointer to a X::Impl
};
struct Impl {}; // Here you declare and define ::Impl
...
int main()
{
X x; // Here X::Impl is still incomplete
}