2

I want to apply the Pimpl idiom with local storage idiom:


mytype.h

class mytype {
 struct Impl;
 enum{ storage = 20; }
 char m_storage[ storage ];
 Impl* PImpl() { return (Impl*)m_storage; }
public:
 mytype();
 ~mytype();
 void myMethod(); 
};

mytype.cpp

#include "mytype.h"
struct mytype::Impl {
int foo;
 void doMethod() { foo = (foo-1)*3; };
}

mytype::mytype() {
new (PImpl()) Impl(); // placement new
//check this at compile-time
static_assert( sizeof(Impl) == mytype::storage ); 
//assert alignment?
}

mytype::~mytype() {
PImpl()->~();
}
void mytype::myMethod() {
 PImpl()->doMethod();
}

the only concern i have with this approach is alignment of m_storage. char is not guaranteed to be aligned in the same way as an int should be. Atomics could have even more restrictive alignment requirements. I'm looking for something better than a char array to declare storage that gives me the ability to also define (and assert) alignment values. Do you know anything of the sort? maybe a boost library already does this?

lurscher
  • 25,930
  • 29
  • 122
  • 185

3 Answers3

4

boost::aligned_storage http://www.boost.org/doc/libs/1_43_0/libs/type_traits/doc/html/boost_typetraits/reference/aligned_storage.html should do the trick.

Is there a reason you aren't just using the normal pimpl approach though?

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • 1
    I would ask it the other way around: is there a reason to use the normal pimpl approach over this approach? I'm gaining one less heap allocation per object. And is not clear that i'm losing anything – lurscher Oct 28 '11 at 16:03
  • 2
    @lurscher: The point of the pimpl idiom is so that in the future if you need to add data members you can do so without having to recompile all the code that uses the class. With your approach the storage size is fixed, so if the total size of your implementation object exceeds 20, it'll certainly break. You've essentially thrown away the benefits the pimpl idiom provides in the first place by doing this. – In silico Oct 28 '11 at 16:09
  • ok good point. It is a bit of a compromise then. Still you are pretty much insulated from implementation details *other than the size and alignment requirements*. Sometimes that is a perfectly acceptable tradeoff between insulation and run-time efficiency – lurscher Oct 28 '11 at 16:14
  • 1
    @Insilico: I think the point of the pimpl idiom is to hide the implementation details. – Mooing Duck Oct 28 '11 at 16:15
  • 3
    @Mooing Duck: True, but even with lurscher's proposed method you are still exposing a very important implementation detail - the size (at least its upper bound) of the implementation, and that can certainly change. – In silico Oct 28 '11 at 16:18
  • 1
    @lurscher Actually, since you statically assert that the size of the char buffer is the same as the size of your impl, you *guarantee* that you have to change your ABI every time the size of the implementation changes. You still have the exact same rebuild and ABI problems as without pimpl, but you added complexity. – Mark B Oct 28 '11 at 16:51
  • 1
    one less heap allocation per object for a huge increase in complexity? That cries out premature optimization. Unless you're under some really tight memory or time constraints, this is not a good idea. – Rob K Oct 28 '11 at 20:17
3

Take a look at boost::aligned_storage. The usage is pretty simple:

#include <boost/aligned_storage.hpp>
#include <boost/type_traits/alignment_of.hpp>


typedef boost::aligned_storage<sizeof(ptime), boost::alignment_of<ptime>::value> storage_type;

using boost::posix_time::ptime;
storage_type unix_epoch_storage_;

new (unix_epoch_storage_.address()) ptime(date(1970, 1, 1));
Nathan Ernst
  • 4,540
  • 25
  • 38
3

The usual solution here is to use a union with the type requiring the most alignment on your system (typically a double):

static int const requiredStorage = 20;
union
{
    unsigned char myStorage[requiredStorage];
    double dummyForAlignment;
};

If you're unsure of which type to use, just throw in all of the basic types, plus a few pointers (to data, to void, to a function) to be sure.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Pointer to virtual member function is not your standard pointer to be sure. – Mooing Duck Oct 28 '11 at 16:14
  • @MooingDuck No, but it's also not a hardware type. Typically, it's the equivalent of a `struct`, with the same alignment the `struct` would have; i.e. the alignment of the strictest member. I tend to ignore it in such cases. – James Kanze Oct 28 '11 at 16:26