开发者

Aligning data on the stack (C++)

开发者 https://www.devze.com 2023-02-13 13:31 出处:网络
This question is specific to the MSVC compiler (specifically 2008), but I\'m interested in non-compiler specific answers too.

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 :)


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.


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]


Are you sure MyType is a valid integer power?

__declspec( align( # ) ) declarator

# is the alignment value. Valid entries are integer powers of two from 1 to 8192 (bytes), such as 2, 4, 8, 16, 32, or 64. declarator is the data that you are declaring as aligned.

-align (C++)


How about alloca()? (Or, more specifically, for MSVC2008, _malloca())?

Allocates memory on the stack. This is a version of _alloca with security enhancements as described in Security Enhancements in the CRT.

Alignment behaviour is not standardised across compilers, but for this one ...

The _malloca routine returns a void pointer to the allocated space, which is guaranteed to be suitably aligned for storage of any type of object.


Had the same problem. You can mash up a macro (oh horrors) that combines align and __alignof in a safe way:

// `align` und `__alignof` cannot be combined - hence this workaround where you also have to specify the alignment manually (but checked)
#define ALIGN_FOR_TYPE( TypeName, TypeAlignment )                           \
    const size_t ALIGN_FOR_TYPE_alignOf##TypeName = __alignof(TypeName);    \
    BOOST_STATIC_ASSERT(ALIGN_FOR_TYPE_alignOf##TypeName == TypeAlignment); \
    __declspec( align( TypeAlignment ) )                                    \
/**/

ALIGN_FOR_TYPE(MyStructType, 4) char StructBuffer[sizeof(MyStructType)];


You don't need to perform any additional "hacks" if you want to declare aligned data on the stack. The compiler will take care of that.

If you want it to be a char array afterwards, simply cast it to char*

Try the following test example to confirm:

#include <stdio.h>

struct UnalignedX {
    int x;
};

__declspec(align(128)) struct AlignedX {
    int x;
};

int main() {
    UnalignedX arr[5];
    AlignedX aarr[5];
    printf("UnalignedX: %x %x\n",arr,arr+1);
    printf("AlignedX: %x %x\n",aarr,aarr+1);
    char *final=(char*)aarr; //this becomes the char array that you asked for
    return 0;
};

On my computer I got the output:

UnalignedX: 14fe68 14fe6c
AlignedX: 14fb80 14fc00

You have to be careful with alignment when allocating data on the heap (either by malloc or new)

__declspec( align( N )) expects N to be a literal. It has to be a number, and not a function call.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号