The struct hack where you have an array of length 0 as the last member of a struct from C90 and C99 is well known, and with the introduction of flexible array members in C99
You can get more or less the same effect using a member
function and a reinterpret_cast:
int* buffer() { return reinterpret_cast(this + 1); }
This has one major defect: it doesn't guarantee correct alignment. For example, something like:
struct Hack
{
char size;
int* buffer() { return reinterpret_cast(this + 1); }
};
is likely to return a mis-aligned pointer. You can work around this by putting the data in the struct in a union with the type whose pointer you are returning. If you have C++11, you can declare:
struct alignas(alignof(int)) Hack
{
char size;
int* buffer() { return reinterpret_cast(this + 1); }
};
(I think. I've never actually tried this, and I could have some details of the syntax wrong.)
This idiom has a second important defect: it does nothing to
ensure that the size field corresponds to the actual size of the
buffer, and worse, there is no real way of using new here. To
correct this, somewhat, you can define a class specific
operator new and operator delete:
struct alignas(alignof(int)) Hack
{
void* operator new( size_t, size_t n );
void operator delete( void* );
Hack( size_t n );
char size;
int* buffer() { return reinterpret_cast(this + 1); }
};
The client code will then have to use placement new to allocate:
Hack* hack = new (20) Hack(20);
The client still has to repeat the size, but he cannot ignore it.
There are also techniques which can be used to prevent creating instances which aren't allocated dynamically, etc., to end up with something like:
struct alignas(alignof(int)) Hack
{
private:
void operator delete( void* p )
{
::operator delete( p );
}
// ban all but dynamic lifetime (and also inheritance, member, etc.)
~Hack() = default;
// ban arrays
void* operator new[]( size_t ) = delete;
void operator delete[]( void* p ) = delete;
public:
Hack( size_t n );
void* operator new( size_t, size_t n )
{
return ::operator new( sizeof(Hack) + n * sizeof(int) );
}
char size;
// Since dtor is private, we need this.
void deleteMe() { delete this; }
int* buffer() { return reinterpret_cast(this + 1); }
};
Given the fundamental dangers of such a class, it is debatable
if so many protective measures are necessary. Even with them,
it's really only usable by someone who fully understands all of
the constraints, and is carefully paying attention. In all but
extreme cases, in very low level code, you'd just make the
buffer a std::vector and be done with it. In all but the
lowest level code, the difference in performance would not be
worth the risk and effort.
EDIT:
As a point of example, g++'s implementation of
std::basic_string uses something very similar to the above,
with a struct containing a reference count, the current size
and the current capacity (three size_t), followed directly by
the character buffer. And since it was written long before
C++11 and alignas/alignof, something like
std::basic_string will crash on some systems (e.g.
a Sparc). (While technically a bug, most people do not consider
this a critical problem.)