Выравнивание данных в стеке (C ++) - PullRequest
13 голосов
/ 27 февраля 2011

Этот вопрос относится только к компилятору MSVC (в частности, к 2008 году), но меня также интересуют ответы, не относящиеся к конкретным компиляторам.

Я пытаюсь выяснить, как выровнять буфер символов настек, основанный на выравнивании некоторого произвольного типа.В идеале код должен выглядеть так:

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

К сожалению, это не работает

ошибка C2059: синтаксическая ошибка: '__builtin_alignof'

компилятору просто не нравятся вложенные операторы.

Моя единственная другая идея - сделать это:

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

Кто-нибудь знает более хороший способ?Кажется, что вещь declspec должна работать, у меня просто неправильный синтаксис или что-то в этом роде?

Спасибо за чтение :)

Ответы [ 6 ]

5 голосов
/ 03 июля 2013

Вы можете использовать std::aligned_storage вместе с std::alignment_of в качестве альтернативы.

#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;

Это поддерживается MSVC 2008 и выше.Если вам нужна переносимость с другими компиляторами не-C ++ 11, вы можете использовать std::tr1::aligned_storage и std::tr1::alignment_of и заголовок <tr1/type_traits>.

В приведенном выше коде AlignedStorage<T>::data будет типом POD (массив char [] в MSVC и GCC) подходящего выравнивания для T и размера T * N.

3 голосов
/ 27 февраля 2011

Обновление

Проверьте ответ Роберта Найта!Использует C ++ 11, но гораздо чище, чем этот ...


Оригинальный ответ

Как насчет этого мерзкого хака:

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;}
};

Выглядит страшно, но вам нужно все это только один раз (желательно в каком-то хорошо скрытом заголовочном файле :)).Тогда вы можете использовать его следующим образом:

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; может появиться в коде, но также и как член более крупного класса (именно так я использую этот хак) и должен также вести себя правильнопри выделении в куче.

Исправление ошибки:

Строка 6 примера: char data[_A_] исправлено в char data[size]

3 голосов
/ 27 февраля 2011

Вы уверены, что MyType является действительной целочисленной степенью?

__declspec( align( # ) ) declarator

# - значение выравнивания. Допустимые записи - целые степени двух от От 1 до 8192 (байты), например 2, 4, 8, 16, 32 или 64. Декларатор это данные что вы объявляете выровненным.

- выравнивание (C ++)

1 голос
/ 29 апреля 2013

Была такая же проблема. Вы можете смешать макрос ( о ужас ), который безопасно сочетает align и __alignof:

// `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)];
1 голос
/ 27 февраля 2011

Как насчет alloca ()?(Или, более конкретно, для MSVC2008: _malloca () )?

Выделяет память в стеке.Это версия _alloca с улучшениями безопасности, как описано в разделе «Улучшения безопасности в CRT».

Поведение выравнивания не стандартизировано между компиляторами, но для этого ...

Процедура _malloca возвращает пустой указатель на выделенное пространство, которое гарантированно выровнено для хранения любого типа объекта.

0 голосов
/ 27 февраля 2011

Вам не нужно выполнять никаких дополнительных «взломов», если вы хотите объявить выровненные данные в стеке. Об этом позаботится компилятор.

Если вы хотите, чтобы впоследствии он был массивом char, просто приведите его к char*

Попробуйте следующий тестовый пример для подтверждения:

#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;
};

На моем компьютере я получил вывод:

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

Вы должны быть осторожны с выравниванием при распределении данных в куче (либо malloc, либо new)

__declspec( align( N )) ожидает, что N будет литералом . Это должен быть номер, а не вызов функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...