Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aligning data on the stack (C++)

Tags:

c++

alignment

This question is specific to the MSVC compiler (specifically 2008), but I'm interested in non-compiler specific answers too.

I'm trying to figure out how to align a char buffer on the stack, based on the alignment of some arbitrary type. Ideally the code would read:

__declspec( align( __alignof(MyType) ) ) char buffer[16*sizeof(MyType)];

Unfortunately, this doesn't work

error C2059: syntax error : '__builtin_alignof'

The compiler just doesn't like the nested statements.

My only other idea is to do this:

char buffer[16*sizeof(MyType)+__alignof(MyType)-1];
char * alignedBuffer = (char*)((((unsigned long)buffer) + __alignof(MyType)-1)&~(__alignof(MyType)-1));

Does anyone know of a nicer way? It seems like the declspec thing should work, do I just have the syntax wrong or something?

Thanks for reading :)

like image 650
JBeFat Avatar asked Feb 27 '11 16:02

JBeFat


People also ask

What does aligning the stack mean?

So to align something in memory means to rearrange data (usually through padding) so that the desired item's address will have enough zero bytes.

Are stack variables aligned?

Stack is word aligned. So, if this is the only variable, the compiler would have to do special work to make the variable non word-aligned. You can look at the dissasembly and see how it is allocated upon the entry into the function.

Why does the stack need to be aligned?

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.


2 Answers

You can use std::aligned_storage together with std::alignment_of as an alternative.

#include <type_traits>

template <class T, int N>
struct AlignedStorage
{
    typename std::aligned_storage<sizeof(T) * N, std::alignment_of<T>::value>::type data;
};

AlignedStorage<int, 16> myValue;

This is supported by MSVC 2008 and up. If you need portability to other non-C++11 compilers you can use std::tr1::aligned_storage and std::tr1::alignment_of and the <tr1/type_traits> header.

In the above code, AlignedStorage<T>::data will be a POD type (a char[] array in MSVC and GCC) of a suitable alignment for T and size T*N.

like image 124
Robert Knight Avatar answered Oct 04 '22 07:10

Robert Knight


Update

Check Robert Knight's answer! Uses C++11 but is much cleaner than this...


Original Answer

How about this nasty hack:

namespace priv {

#define PRIVATE_STATICMEM(_A_) \
    template <size_t size> \
    struct StaticMem<size,_A_> { \
      __declspec(align(_A_)) char data[size]; \
      void *operator new(size_t parSize) { \
        return _aligned_malloc(parSize,_A_); \
      } \
      void operator delete(void *ptr) { \
        return _aligned_free(ptr); \
      } \
    };

    template <size_t size, size_t align> struct StaticMem {};
    template <size_t size> struct StaticMem<size,1> {char data[size];};

    PRIVATE_STATICMEM(2)
    PRIVATE_STATICMEM(4)
    PRIVATE_STATICMEM(8)
    PRIVATE_STATICMEM(16)
    PRIVATE_STATICMEM(32)
    PRIVATE_STATICMEM(64)
    PRIVATE_STATICMEM(128)
    PRIVATE_STATICMEM(256)
    PRIVATE_STATICMEM(512)
    PRIVATE_STATICMEM(1024)
    PRIVATE_STATICMEM(2048)
    PRIVATE_STATICMEM(4096)
    PRIVATE_STATICMEM(8192)

}

template <typename T, size_t size> struct StaticMem : public priv::StaticMem<sizeof(T)*size,__alignof(T)> {
    T *unhack() {return (T*)this;}
    T &unhack(size_t idx) {return *(T*)(data+idx*sizeof(T));}
    const T &unhack() const {return *(const T*)this;}
    const T &unhack(size_t idx) const {return *(const T*)(data+idx*sizeof(T));}
    StaticMem() {}
    StaticMem(const T &init) {unhack()=init;}
};

Looks scary, but you need all that only once (preferably in some well hidden header file :) ). Then you can use it in the following way:

StaticMem<T,N> array; //allocate an uninitialized array of size N for type T
array.data //this is a raw char array
array.unhack() //this is a reference to first T object in the array
array.unhack(5) //reference to 5th T object in the array

StaticMem<T,N> array; can appear in the code, but also as a member of some bigger class (that's how I use this hack) and should also behave correctly when allocated on the heap.

Bug fix:

Line 6 of the example: char data[_A_] corrected into char data[size]

like image 32
CygnusX1 Avatar answered Oct 04 '22 06:10

CygnusX1