Reference counted structure exposing a POD interface - c++

I currently have a structure that declares a copy constructor, and a destructor, and keeps a reference count to a pimpl object. This allows me to pass this structure around by value. I need to refactor it because I want it to have a POD interface because it's part of a library that now needs to be used from code that uses an older C++ flavor.
Because of this, I have to remove both the copy constructor and the desctructor. I can't figure out how I can keep this nice "pass by value", multiple ownership mechanism after I have removed that. Shared pointers are not an option because the structure is used as parameter to the method of other classes that also need to be seen as POD from the perspective of the user of the library.
struct Foo {
Foo(const Foo&);
const Foo& operator=(const Foo&);
~ Foo();
private:
void* pimpl;
};
struct MainLibrary {
void DoSomething(Foo param);
}
The user code now is like:
MainLibrary ml;
{
Foo a;
ml.doSomething(a);
}
at this point, the a variable can be kept for a long time inside the main library. For efficiency, the internals of Foo can't be deep copied each time, that's why the pimpl implementation keeps a reference counter that is incremented each time the instance of Foo is copied, decremented each time the instance of Foo is destroyed.

Too much for a comment... something like below. Client include foo03.h and their usage should remain unaffected. Your C++11 implementation is invoked via a "C" layer. You can find lots of examples if you search for "exposing C++ code to C" or similar....
foo03.h:
extern "C"
{
void* foo_constructor();
void* foo_constructor2(void* rhs);
void foo_assign(void* lhs, void* rhs);
void foo_destructor(void* p_this);
}
struct Foo {
Foo() { p_ = foo_constructor(); }
Foo(const Foo& rhs) { p_ = foo_constructor2(rhs.p_); }
const Foo& operator=(const Foo& rhs) { foo_assign(p_, rhs.p_); return *this; }
~Foo() { foo_destructor(p_); }
private:
void* p_;
};
foo11.h:
// up to you whether you call this Foo (relying on mangling differences to avoid conflicts when
// linking with C++03 objects), FooImpl, put it in a namespace ala Impl::Foo etc..
struct FooImpl {
FooImpl() { ... }
FooImpl(const FooImpl&) { ... }
const FooImpl& operator=(const FooImpl& rhs) { ... }
~FooImpl() { ... }
};
extern "C"
{
void* foo_constructor() { return new FooImpl(); }
void* foo_constructor2(void* rhs) { return new FooImpl(*(FooImpl*)rhs); }
void foo_assign(void* lhs, void* rhs) { *(FooImpl*)lhs = *(FooImpl*)rhs; }
void foo_destructor(void* p_this) { delete (FooImpl*)p_this; }
}

Related

Wrap a C header in C++

Suppose I have some C code that I want to wrap using C++ without exposing the C code to the user.
My first attempt was to manually include all headers used in the C code before including the C headers inside a namespace, to hide them from the global scope. This seemed to initially work and compile, however, after trying to use some of the defined structures, I got compilation and linking errors since it couldn't find their definitions.
So my question is, either how do I make the above work, or how else do I make my wrapper in a way not to expose the C code to the user of the C++ headers.
I should mentioned, that I need the structures and other types in my declarations, so I can't include the header in the cpp file only, for example:
class Wrapper {
c_struct* _internal;
public:
void proxy() {
c_function(this->_internal);
}
}
You could use the pimpl idiom to hide all the C headers and structs inside your .cpp file.
Example .hpp file:
#pragma once
#include <memory>
class Wrapper {
public:
Wrapper();
Wrapper(const Wrapper&);
Wrapper(Wrapper&&) noexcept = default;
Wrapper& operator=(const Wrapper&);
Wrapper& operator=(Wrapper&&) noexcept = default;
~Wrapper() = default;
void proxy();
private:
struct internal_type; // forward declaration
std::unique_ptr<internal_type> m_internal; // pointer to implementation
};
Example .cpp file:
#include "Wrapper.hpp"
//#include the C header here.
/*
I assume it contains declarations like below:
struct c_struct;
struct c_struct* c_struct_create();
struct c_struct* c_struct_clone(struct c_struct*); // if it can be cloned
void c_struct_destroy(struct c_struct*);
void c_function(struct c_struct*);
*/
#include <utility>
// the definition of Wrapper::internal_type may inherit from c_struct - or
// use composition like below:
struct Wrapper::internal_type {
internal_type() : handle(c_struct_create()) {}
internal_type(const internal_type& rhs) : handle(c_struct_clone(rhs.handle)) {}
internal_type(internal_type&& rhs) noexcept :
handle(std::exchange(rhs.handle, nullptr)) {}
internal_type& operator=(const internal_type& rhs) {
if(this == &rhs) return *this;
if(handle) c_struct_destroy(handle);
handle = c_struct_clone(rhs.handle);
return *this;
}
internal_type& operator=(internal_type&& rhs) noexcept {
std::swap(handle, rhs.handle);
return *this;
}
~internal_type() { if(handle) c_struct_destroy(handle); }
void proxy() { c_function(handle); }
private:
c_struct* handle;
};
// default constructor
Wrapper::Wrapper() : m_internal(std::make_unique<internal_type>()) {}
// copy constructor
Wrapper::Wrapper(const Wrapper& o)
: m_internal(std::make_unique<internal_type>(*o.m_internal)) {}
// copy assignment operator
Wrapper& Wrapper::operator=(const Wrapper& rhs) {
*m_internal = *rhs.m_internal;
return *this;
}
// the proxy functions just forward to the internal implementation
// in internal_type:
void Wrapper::proxy() {
m_internal->proxy();
}

Should I delete the move constructor and the move assignment of a smart pointer?

I'm implementing a simple smart pointer, which basically keeps track of the number of references to a pointer that it handles.
I know I could implement move semantics, but I don't think it makes sense as copying a smart pointer is very cheap. Especially considering that it introduces opportunities to produce nasty bugs.
Here's my C++11 code (I omitted some inessential code). General comments are welcome as well.
#ifndef SMART_PTR_H_
#define SMART_PTR_H_
#include <cstdint>
template<typename T>
class SmartPtr {
private:
struct Ptr {
T* p_;
uint64_t count_;
Ptr(T* p) : p_{p}, count_{1} {}
~Ptr() { delete p_; }
};
public:
SmartPtr(T* p) : ptr_{new Ptr{p}} {}
~SmartPtr();
SmartPtr(const SmartPtr<T>& rhs);
SmartPtr(SmartPtr<T>&& rhs) =delete;
SmartPtr<T>& operator=(const SmartPtr<T>& rhs);
SmartPtr<T>& operator=(SmartPtr<T>&& rhs) =delete;
T& operator*() { return *ptr_->p_; }
T* operator->() { return ptr_->p_; }
uint64_t Count() const { return ptr_->count_; }
const T* Raw() const { return ptr_->p_; }
private:
Ptr* ptr_;
};
template<typename T>
SmartPtr<T>::~SmartPtr() {
if (!--ptr_->count_) {
delete ptr_;
}
ptr_ = nullptr;
}
template<typename T>
SmartPtr<T>::SmartPtr(const SmartPtr<T>& rhs) : ptr_{rhs.ptr_} {
++ptr_->count_;
}
template<typename T>
SmartPtr<T>& SmartPtr<T>::operator=(const SmartPtr<T>& rhs) {
if (this != &rhs) {
if (!--ptr_->count_) {
delete ptr_;
}
ptr_ = rhs.ptr_;
++ptr_->count_;
}
return *this;
}
#endif // SMART_PTR_H_
Guideline
Never delete the special move members.
In typical code (such as in your question), there are two motivations to delete the move members. One of those motivations produces incorrect code (as in your example), and for the other motivation the deletion of the move members is redundant (does no harm nor good).
If you have a copyable class and you don't want move members, simply don't declare them (which includes not deleting them). Deleted members are still declared. Deleted members participate in overload resolution. Members not present don't. When you create a class with a valid copy constructor and a deleted move member, you can't return it by value from a function because overload resolution will bind to the deleted move member.
Sometimes people want to say: this class is neither movable nor copyable. It is correct to delete both the copy and the move members. However just deleting the copy members is sufficient (as long as the move members are not declared). Declared (even deleted) copy members inhibit the compiler from declaring move members. So in this case the deleted move members are simply redundant.
If you declare deleted move members, even if you happen to pick the case where it is redundant and not incorrect, every time someone reads your code, they need to re-discover if your case is redundant or incorrect. Make it easier on readers of your code and never delete the move members.
The incorrect case:
struct CopyableButNotMovble
{
// ...
CopyableButNotMovble(const CopyableButNotMovble&);
CopyableButNotMovble& operator=(const CopyableButNotMovble&);
CopyableButNotMovble(CopyableButNotMovble&&) = delete;
CopyableButNotMovble& operator=(CopyableButNotMovble&&) = delete;
// ...
};
Here is example code you probably expected to work with CopyableButNotMovble but will fail at compile time:
#include <algorithm>
#include <vector>
struct CopyableButNotMovble
{
// ...
CopyableButNotMovble(const CopyableButNotMovble&);
CopyableButNotMovble& operator=(const CopyableButNotMovble&);
CopyableButNotMovble(CopyableButNotMovble&&) = delete;
CopyableButNotMovble& operator=(CopyableButNotMovble&&) = delete;
CopyableButNotMovble(int);
// ...
friend bool operator<(CopyableButNotMovble const& x, CopyableButNotMovble const& y);
};
int
main()
{
std::vector<CopyableButNotMovble> v{3, 2, 1};
std::sort(v.begin(), v.end());
}
In file included from test.cpp:1:
algorithm:3932:17: error: no
matching function for call to 'swap'
swap(*__first, *__last);
^~~~
algorithm:4117:5: note: in
instantiation of function template specialization 'std::__1::__sort<std::__1::__less<CopyableButNotMovble,
CopyableButNotMovble> &, CopyableButNotMovble *>' requested here
__sort<_Comp_ref>(__first, __last, __comp);
^
algorithm:4126:12: note: in
instantiation of function template specialization 'std::__1::sort<CopyableButNotMovble *,
std::__1::__less<CopyableButNotMovble, CopyableButNotMovble> >' requested here
_VSTD::sort(__first, __last, __less<typename iterator_traits<_RandomAccessIterator>::value_type>());
^
...
(many nasty error messages from deep inside your std::lib)
The correct way to do this is:
struct CopyableButNotMovble
{
// ...
CopyableButNotMovble(const CopyableButNotMovble&);
CopyableButNotMovble& operator=(const CopyableButNotMovble&);
// ...
};
The redundant case:
struct NeitherCopyableNorMovble
{
// ...
NeitherCopyableNorMovble(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble& operator=(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble(NeitherCopyableNorMovble&&) = delete;
NeitherCopyableNorMovble& operator=(NeitherCopyableNorMovble&&) = delete;
// ...
};
The more readable way to do this is:
struct NeitherCopyableNorMovble
{
// ...
NeitherCopyableNorMovble(const NeitherCopyableNorMovble&) = delete;
NeitherCopyableNorMovble& operator=(const NeitherCopyableNorMovble&) = delete;
// ...
};
It helps if you make a practice of always grouping all 6 of your special members near the top of your class declaration, in the same order, skipping those you don't want to declare. This practice makes it easier for readers of your code to quickly determine that you have intentionally not declared any particular special member.
For example, here is the pattern I follow:
class X
{
// data members:
public:
// special members
~X();
X();
X(const X&);
X& operator=(const X&);
X(X&&);
X& operator=(X&&);
// Constructors
// ...
};
Here is a more in-depth explanation of this declaration style.

How to make an object inside a function and return it to main() without the destructor delete it in c++?

I have this class:
class foo{
public:
foo();
foo(const int& var);
foo(const foo& var);
~foo();
foo retSumOf(const foo& var1, const foo& var2);
private:
int a;
char* x;
};
and this meber function:
foo foo::retSumOf(const foo& var1, const foo& var2){
//Make somehow a foo object,
//lets name it 'result'
result.a = var1.a + var2.a;
return (result);
}
I want to do this in main:
main(){
foo a, b;
foo* c;
a.a = 1;
b.a = 2;
c = retSumOf(a, b);
cout << "sum =" << c->a;
}
without to invoke the (overloaded) copy constructor!
Is there a way in C++ to create an object dynamically from a function and return his address? Without the constructor to delete it at the end of the invocation of retSumOf()?
Why?
Yes, you can return a (smart) pointer, like Jeremy says, but why would you do that? Why not instead correctly implement the copy and move functions? You already have a constructor; by the rule of five you should implement the other four functions anyway, or suppress them.
Modify the function as follows:
foo* foo::retSumOf(const foo& var1, const foo& var2){
//Make somehow a foo object,
//lets name it 'result'
foo* result = new foo();
result->a = var1.a + var2.a;
return (result);
}
There are a couple of ways, first:
You can use the return value as an argument:
void foo::computeSumOf(foo & result, const foo& var1, const foo& var2) ...
Another way is to take advantage of the RVO optimization
foo foo::retSumOf(const foo& var1, const foo& var2)
{
return foo(var1.a + var2.a);
}
//...
foo x = someFoo.retuSomOf(a,b);
Third (if you can use c++ 11) you can use write move constructor and assignment to avoid copy constructor. By doing this you can optimize the unnecessary copy of members and just "move" the memory from one instance to another . You can find more info here.
class foo{
public:
foo(foo && rValue) { ... };
foo& operator = (foo && rValue) { ... };
...
};
foo foo::retSumOf(const foo& var1, const foo& var2){
foo result;
//same code
return result;
}
Lastly, you can use shared_ptr or other smart pointers (like intrusive_ptr)
std::shared_ptr<foo> foo::retSumOf(const foo& var1, const foo& var2){
std::shared_ptr<foo> result = new foo;
result->a = ...
return result;
}
You could either declare it as static:
static myClass objName;
or use new (preferred):
myClass* objName = new myClass;
return objName
If you use the second method, you need to modify your function to return a pointer, rather than an object.
You can return the value from the function as a reference to foo and save it in a const foo&. The const-reference will prolong the lifespan of the foo instance to the lifespan of the variable.
foo& foo::retSumOf(const foo& var1, const foo& var2)
...
const foo& c = retSumOf(a, b);
EDIT
Turns out this does not work: http://ideone.com/WM3bIe
Just do this
class foo
{
...
friend foo retSumOf(const foo& var1, const foo& var2);
...
};
foo retSumOf(const foo& var1, const foo& var2);
{
foo result;
result.a = var1.a + var2.a;
return (result);
}
and enable compiler optimization.
Since there's only one return statement in your function, and result is local, the compiler will create it on the stack in the place of the return value, avoiding the copy.
No need to worry about that. (and yes, no need to call "move").
If you want a more "functional" approach, give to foo a constructor that defines the member values and use accordngly:
class foo
{
// you existing code andd ...
foo(int i, const char* s) :a(i), x(s)
{}
};
so that you can do
foo retSumOf(const foo& var1, const foo& var2)
{ return foo(var1.a + var1.b, nullptr); }
It it even make sense, you can name retSumOf just operator+ and have c = a+b;
In any case, avoid to have c as a naked pointer: it will never be clear who has to delete the received object. It only needs to be a value.
Don't ever use char* as a member: it is not clear who owns the data after an assignment or a copy. Use std::string instead.
Not sure if my answer is right but it actually seems to work all the time for me:
#include <iostream>
class Foo
{
public:
Foo() {std::cout<<"HEY!";}
~Foo() {}
};
Foo&& GetFoo()
{
return std::move(Foo());
}
int main()
{
GetFoo();
return 0;
}
I usually just move an object created within a function.. Just so that I can avoid pointers as much as possible.. Otherwise I'd use a Smart pointer but the above is what I normally do.

Manually define only part of copy constructor and assignment operator

I'm wondering if there is a way to implement copy constructors and assignment operators such that only a small modification is needed when these are redefined for a class.
For example, consider a class as such:
class Foo {
private:
int* mInt_ptr;
/* many other member variables
of different types that aren't
pointers */
public:
Foo();
Foo(const Foo&);
Foo& operator=(const Foo&);
~Foo();
};
Now, in order to deal with the pointer mInt_ptr I would need to handle it appropriately in the copy constructor and assignment operator. However, the rest of the member variables are safe to do a shallow copy of. Is there a way to do this automatically?
Once a class becomes large it may become tedious and unwieldy to explicitly write out the operations to copy the non-pointer member variables, so I'm wondering if there is a way to write, say, a copy constructor such as:
Foo::Foo(const Foo& tocopy)
{
mInt_ptr = new int(*tocopy.mInt_ptr);
/* Do shallow copy here somehow? */
}
rather than the explicit form of:
Foo::Foo(const Foo& tocopy)
{
mInt_ptr = new int(*tocopy.mInt_ptr);
mVar1 = tocopy.mVar1;
mVar2 = tocopy.mVar2;
...
...
mVarN = tocopy.mVarN;
}
Generally, don't use raw pointers, for exactly the reason that you're now fighting with. Instead, use a suitable smart pointer, and use copy-swap assignment:
class Foo
{
int a;
Zip z;
std::string name;
value_ptr<Bar> p;
public:
Foo(Foo const &) = default;
Foo & operator=(Foo rhs)
{
rhs.swap(*this);
return *this;
}
void swap(Foo & rhs)
{
using std::swap;
swap(a, rhs.a);
swap(z, rhs.z);
swap(name, rhs.name);
swap(p, rhs.p);
}
};
namespace std { template <> void swap<Foo>(Foo & a, Foo & b) { a.swap(b); } }
The value_ptr could be a full-blown implementation, or something simple such as this:
template <typename T> // suitable for small children,
class value_ptr // but not polymorphic base classes.
{
T * ptr;
public:
constexpr value_ptr() : ptr(nullptr) { }
value_ptr(T * p) noexcept : ptr(p) { }
value_ptr(value_ptr const & rhs) : ptr(::new T(*rhs.ptr)) { }
~value_ptr() { delete ptr; }
value_ptr & operator=(value_ptr rhs) { rhs.swap(*this); return *this; }
void swap(value_ptr & rhs) { std::swap(ptr, rhs.ptr); }
T & operator*() { return *ptr; }
T * operator->() { return ptr; }
};
How about you wrap all the shallow-copy bits in a small helper struct and use the default copy behaviour there.
class Foo {
private:
int* mInt_ptr;
struct helper_t
/* many other member variables
of different types that aren't
pointers */
} mHelper;
public:
Foo();
Foo(const Foo&);
Foo& operator=(const Foo&);
~Foo();
};
Foo::Foo(const Foo& tocopy)
{
mInt_ptr = new int(*tocopy.mInt_ptr);
mHelper = tocopy.mHelper;
}
Using better primitives, as Kerrek suggested, seems like better design though. This is just another possibility.
Regardless if you use raw pointers or smart pointers the Kerrek's solution is right in the sense that you should make a copy constructor, destructor and swap and implement assignment using those:
class Foo
{
private:
int* mInt_ptr;
// many other member variables
// of different types
public:
Foo()
: mInt_ptr(NULL)
// initialize all other members
{}
Foo(const Foo& that)
: mInt_ptr(new int(*that.mInt_ptr) )
// copy-construct all other members
{}
Foo& operator=(const Foo& that)
{
// you may check if(this == &that) here
Foo(that).swap(*this);
return *this;
}
~Foo()
{
delete mInt_ptr;
// and release other resources
}
void swap(Foo& that)
{
std::swap(mInt_ptr, that.mInt_ptr);
// swap all members
}
};
The members are inline here just to keep it compact, usually it is not advisable to burden class definition with inline member definitions.

STL-friendly pImpl class?

I am maintaining a project that can take a considerable time to build so am trying to reduce dependencies where possible. Some of the classes could make use if the pImpl idiom and I want to make sure I do this correctly and that the classes will play nicely with the STL (especially containers.) Here is a sample of what I plan to do - does this look OK? I am using std::auto_ptr for the implementation pointer - is this acceptable? Would using a boost::shared_ptr be a better idea?
Here is some code for a SampleImpl class that uses classes called Foo and Bar:
// SampleImpl.h
#ifndef SAMPLEIMPL_H
#define SAMPLEIMPL_H
#include <memory>
// Forward references
class Foo;
class Bar;
class SampleImpl
{
public:
// Default constructor
SampleImpl();
// Full constructor
SampleImpl(const Foo& foo, const Bar& bar);
// Copy constructor
SampleImpl(const SampleImpl& SampleImpl);
// Required for std::auto_ptr?
~SampleImpl();
// Assignment operator
SampleImpl& operator=(const SampleImpl& rhs);
// Equality operator
bool operator==(const SampleImpl& rhs) const;
// Inequality operator
bool operator!=(const SampleImpl& rhs) const;
// Accessors
Foo foo() const;
Bar bar() const;
private:
// Implementation forward reference
struct Impl;
// Implementation ptr
std::auto_ptr<Impl> impl_;
};
#endif // SAMPLEIMPL_H
// SampleImpl.cpp
#include "SampleImpl.h"
#include "Foo.h"
#include "Bar.h"
// Implementation definition
struct SampleImpl::Impl
{
Foo foo_;
Bar bar_;
// Default constructor
Impl()
{
}
// Full constructor
Impl(const Foo& foo, const Bar& bar) :
foo_(foo),
bar_(bar)
{
}
};
SampleImpl::SampleImpl() :
impl_(new Impl)
{
}
SampleImpl::SampleImpl(const Foo& foo, const Bar& bar) :
impl_(new Impl(foo, bar))
{
}
SampleImpl::SampleImpl(const SampleImpl& sample) :
impl_(new Impl(*sample.impl_))
{
}
SampleImpl& SampleImpl::operator=(const SampleImpl& rhs)
{
if (this != &rhs)
{
*impl_ = *rhs.impl_;
}
return *this;
}
bool SampleImpl::operator==(const SampleImpl& rhs) const
{
return impl_->foo_ == rhs.impl_->foo_ &&
impl_->bar_ == rhs.impl_->bar_;
}
bool SampleImpl::operator!=(const SampleImpl& rhs) const
{
return !(*this == rhs);
}
SampleImpl::~SampleImpl()
{
}
Foo SampleImpl::foo() const
{
return impl_->foo_;
}
Bar SampleImpl::bar() const
{
return impl_->bar_;
}
You should consider using copy-and-swap for assignment if it's possible that Foo or Bar might throw as they're being copied. Without seeing the definitions of those classes, it's not possible to say whether they can or not. Without seeing their published interface, it's not possible to say whether they will in future change to do so, without you realising.
As jalf says, using auto_ptr is slightly dangerous. It doesn't behave the way you want on copy or assignment. At a quick look, I don't think your code ever allows the impl_ member to be copied or assigned, so it's probably OK.
If you can use scoped_ptr, though, then the compiler will do that tricky job for you of checking that it's never wrongly modified. const might be tempting, but then you can't swap.
There are a couple of problems with the Pimpl.
First of all, though not evident: if you use Pimpl, you will have to define the copy constructor / assignment operator and destructor (now known as "Dreaded 3")
You can ease that by creating a nice template class with the proper semantic.
The problem is that if the compiler sets on defining one of the "Dreaded 3" for you, because you had used forward declaration, it does know how to call the "Dreaded 3" of the object forward declared...
Most surprising: it seems to work with std::auto_ptr most of the times, but you'll have unexpected memory leaks because the delete does not work. If you use a custom template class though, the compiler will complain that it cannot find the needed operator (at least, that's my experience with gcc 3.4.2).
As a bonus, my own pimpl class:
template <class T>
class pimpl
{
public:
/**
* Types
*/
typedef const T const_value;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
/**
* Gang of Four
*/
pimpl() : m_value(new T) {}
explicit pimpl(const_reference v) : m_value(new T(v)) {}
pimpl(const pimpl& rhs) : m_value(new T(*(rhs.m_value))) {}
pimpl& operator=(const pimpl& rhs)
{
pimpl tmp(rhs);
swap(tmp);
return *this;
} // operator=
~pimpl() { delete m_value; }
void swap(pimpl& rhs)
{
pointer temp(rhs.m_value);
rhs.m_value = m_value;
m_value = temp;
} // swap
/**
* Data access
*/
pointer get() { return m_value; }
const_pointer get() const { return m_value; }
reference operator*() { return *m_value; }
const_reference operator*() const { return *m_value; }
pointer operator->() { return m_value; }
const_pointer operator->() const { return m_value; }
private:
pointer m_value;
}; // class pimpl<T>
// Swap
template <class T>
void swap(pimpl<T>& lhs, pimpl<T>& rhs) { lhs.swap(rhs); }
Not much considering boost (especially for the cast issues), but there are some niceties:
proper copy semantic (ie deep)
proper const propagation
You still have to write the "Dreaded 3". but at least you can treat it with value semantic.
EDIT: Spurred on by Frerich Raabe, here is the lazy version, when writing the Big Three (now Four) is a hassle.
The idea is to "capture" information where the full type is available and use an abstract interface to make it manipulable.
struct Holder {
virtual ~Holder() {}
virtual Holder* clone() const = 0;
};
template <typename T>
struct HolderT: Holder {
HolderT(): _value() {}
HolderT(T const& t): _value(t) {}
virtual HolderT* clone() const { return new HolderT(*this); }
T _value;
};
And using this, a true compilation firewall:
template <typename T>
class pimpl {
public:
/// Types
typedef T value;
typedef T const const_value;
typedef T* pointer;
typedef T const* const_pointer;
typedef T& reference;
typedef T const& const_reference;
/// Gang of Five (and swap)
pimpl(): _holder(new HolderT<T>()), _p(this->from_holder()) {}
pimpl(const_reference t): _holder(new HolderT<T>(t)), _p(this->from_holder()) {}
pimpl(pimpl const& other): _holder(other->_holder->clone()),
_p(this->from_holder())
{}
pimpl(pimpl&& other) = default;
pimpl& operator=(pimpl t) { this->swap(t); return *this; }
~pimpl() = default;
void swap(pimpl& other) {
using std::swap;
swap(_holder, other._holder);
swap(_p, other._p)
}
/// Accessors
pointer get() { return _p; }
const_pointer get() const { return _p; }
reference operator*() { return *_p; }
const_reference operator*() const { return *_p; }
pointer operator->() { return _p; }
const_pointer operator->() const { return _p; }
private:
T* from_holder() { return &static_cast< HolderT<T>& >(*_holder)._value; }
std::unique_ptr<Holder> _holder;
T* _p; // local cache, not strictly necessary but avoids indirections
}; // class pimpl<T>
template <typename T>
void swap(pimpl<T>& left, pimpl<T>& right) { left.swap(right); }
I've been struggling with the same question. Here's what I think the answer is:
You can do what you are suggesting, so long as you define the copy and assignment operators to do sensible things.
It's important to understand that the STL containers create copies of things. So:
class Sample {
public:
Sample() : m_Int(5) {}
void Incr() { m_Int++; }
void Print() { std::cout << m_Int << std::endl; }
private:
int m_Int;
};
std::vector<Sample> v;
Sample c;
v.push_back(c);
c.Incr();
c.Print();
v[0].Print();
The output from this is:
6
5
That is, the vector has stored a copy of c, not c itself.
So, when you rewrite it as a PIMPL class, you get this:
class SampleImpl {
public:
SampleImpl() : pimpl(new Impl()) {}
void Incr() { pimpl->m_Int++; }
void Print() { std::cout << m_Int << std::endl; }
private:
struct Impl {
int m_Int;
Impl() : m_Int(5) {}
};
std::auto_ptr<Impl> pimpl;
};
Note I've mangled the PIMPL idiom a bit for brevity. If you try to push this into a vector, it still tries to create a copy of the SampleImpl class. But this doesn't work, because std::vector requires that the things it store provide a copy constructor that doesn't modify the thing it's copying.
An auto_ptr points to something that is owned by exactly one auto_ptr. So when you create a copy of an auto_ptr, which one now owns the underlying pointer? The old auto_ptr or the new one? Which one is responsible for cleaning up the underlying object? The answer is that ownership moves to the copy and the original is left as a pointer to nullptr.
What auto_ptr is missing that prevents its use in a vector is copy constructor taking a const reference to the thing being copied:
auto_ptr<T>(const auto_ptr<T>& other);
(Or something similar - can't remember all the template parameters). If auto_ptr did provide this, and you tried to use the SampleImpl class above in the main() function from the first example, it would crash, because when you push c into the vector, the auto_ptr would transfer ownership of pimpl to the object in the vector and c would no longer own it. So when you called c.Incr(), the process would crash with a segmentation fault on the nullptr dereference.
So you need to decide what the underlying semantics of your class are. If you still want the 'copy everything' behaviour, then you need to provide a copy constructor that implements that correctly:
SampleImpl(const SampleImpl& other) : pimpl(new Impl(*(other.pimpl))) {}
SampleImpl& operator=(const SampleImpl& other) { pimpl.reset(new Impl(*(other.pimpl))); return *this; }
Now when you try to take a copy of a SampleImpl, you also get a copy of its Impl struct, owned by the copy SampleImpl. If you're taking an object that had lots of private data members and was used in STL containers and turning it into a PIMPL class, then this is probably what you want, as it provides the same semantics as the original. But note that pushing the object into a vector will be considerably slower as there is now dynamic memory allocation involved in copying the object.
If you decide you don't want this copy behaviour, then the alternative is for the copies of SampleImpl to share the underlying Impl object. In this case, it's not longer clear (or even well-defined) which SampleImpl object owns the underlying Impl. If ownership doesn't clearly belong in one place, then std::auto_ptr is the wrong choice for storing it
and you need to use something else, probably a boost template.
Edit: I think the above copy constructor and assignment operator are exception-safe so long as ~Impl doesn't throw an exception. This should always be true of your code anyway.