In C++03 code, how would I portably implement an unsigned char[sizeof(T)]
buffer that has the same size and alignment as that of a given type T
?
For example:
template<class T>
void test()
{
unsigned char buffer[sizeof(T)]; // <----- how do I ensure this is aligned?
if (some_condition())
{
T *const obj = new(buffer) T();
// ...
obj->~T();
}
else { /* use 'buffer' for something else */ }
}
Is this even possible, or are you forced to use compiler extensions in order to implement this?
Alignment is limiting addresses where data can be placed and is not limited to stacks. For example, 4-byte alignment would mean that all addresses have the lowest 2 bits always 0. The alignment often corresponds to the memory bus width in the hardware which can be several bytes wide.
General Byte Alignment RulesStructures between 5 and 8 bytes of data should be padded so that the total structure is 8 bytes. Structures between 9 and 16 bytes of data should be padded so that the total structure is 16 bytes. Structures greater than 16 bytes should be padded to 16 byte boundary.
new and malloc, by default, align address to 8 bytes (x86) or 16 bytes (x64), which is the optimal for most complex data.
A memory access is said to be aligned when the data being accessed is n bytes long and the datum address is n-byte aligned. When a memory access is not aligned, it is said to be misaligned. Note that by definition byte memory accesses are always aligned.
In his Guru Of The Week #28 column, Herb Sutter uses a union but it's less robust than Boost's efforts.
Boost's aligned_storage solves the gory details for you. If you look at its implementation, you'll see it uses MSCV's __alignof
or GCC's __alignof__
as well as another template: type_with_alignment
.
From my own codebase, I once used (derived from the GOTW link above):
#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 150020706)
# pragma warning(push)
# pragma warning(disable: 4371)
#endif // #if (defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 150020706)
union AlignedStorage
{
char storage[sizeof(T)];
int16 dummy0;
int32 dummy1;
int64 dummy2;
float dummy3;
double dummy4;
long double dummy5;
void (*dummy6)();
struct dummy7;
int dummy7::*dummy8;
#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140050215)
# pragma warning(push)
# pragma warning(disable: 4121)
#endif // #if (defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140050215)
int (dummy7::*dummy9)(int);
#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140050215)
# pragma warning(pop)
#endif // #if (defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140050215)
}; // AlignedStorage
#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 150020706)
# pragma warning(pop)
#endif // #if (defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 150020706)
These days I would just rely on Boost since it likely covers many more corner cases and compiler specificities
The reason compiler extensions like __alignof
and __attribute__((aligned(n))
exist is that determining and enforcing alignment can not be implemented portably in C and C++. I.e. the standard requires no means to do that.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With