Marcel Mueller
2024-06-23 12:37:00 UTC
Quite often I have the following requirement:
I need a data structure that holds a single header H together with N
elements E. N is constant but not constexpr so just using std::array is
not an option.
Use cases:
- A reference counted store for (small) images. The reference count, the
data size and maybe some meta information is the header while the
picture data is just raw storage.
- A reference counted string with refcount and size as header and an
array of the char type as data.
Normally this always requires two allocations for each object, one for
the header and one for the data. When holding millions of small objects
this can be a considerable overhead.
In good old C this was usually handled by
struct Storage
{ H Header;
E Data[];
};
But this does not work for C++ types with constructors/destructors.
new Storage() is not valid.
There is a dirty hack by writing a custom operator new that takes an
additional argument for N.
class Storage
{public:
H Header;
size_t n;
E Data[];
void* operator new(std::size_t sz, std::size_t N);
void operator delete(void* ptr);
};
The drawback is that ~Storage() must know N to call ~E(). In order to
work the operator new needs to store N somewhere in Storage which is not
the idea of an allocation operator.
Furthermore the size calculation in operator new needs offsetof(Storage,
Data), which is likely to be UB when Storage and H are no trivial types.
The size passed to operator new is not sufficient because the offset
might be different due to tail padding or tail optimization (e.g if E is
just char) or alignment requirements of E.
Any other ideas?
Marcel
I need a data structure that holds a single header H together with N
elements E. N is constant but not constexpr so just using std::array is
not an option.
Use cases:
- A reference counted store for (small) images. The reference count, the
data size and maybe some meta information is the header while the
picture data is just raw storage.
- A reference counted string with refcount and size as header and an
array of the char type as data.
Normally this always requires two allocations for each object, one for
the header and one for the data. When holding millions of small objects
this can be a considerable overhead.
In good old C this was usually handled by
struct Storage
{ H Header;
E Data[];
};
But this does not work for C++ types with constructors/destructors.
new Storage() is not valid.
There is a dirty hack by writing a custom operator new that takes an
additional argument for N.
class Storage
{public:
H Header;
size_t n;
E Data[];
void* operator new(std::size_t sz, std::size_t N);
void operator delete(void* ptr);
};
The drawback is that ~Storage() must know N to call ~E(). In order to
work the operator new needs to store N somewhere in Storage which is not
the idea of an allocation operator.
Furthermore the size calculation in operator new needs offsetof(Storage,
Data), which is likely to be UB when Storage and H are no trivial types.
The size passed to operator new is not sufficient because the offset
might be different due to tail padding or tail optimization (e.g if E is
just char) or alignment requirements of E.
Any other ideas?
Marcel