Выравнивание данных в стеке (С++)
Этот вопрос специфичен для компилятора MSVC (в частности, 2008), но я также заинтересован в ответах, отличных от компилятора.
Я пытаюсь выяснить, как выровнять буфер char в стеке, основываясь на выравнивании некоторого произвольного типа. В идеале код будет читать:
__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 должна работать, просто я имею синтаксис неправильно или что-то в этом роде?
Спасибо за чтение:)
Ответы
Ответ 1
Обновление
Отметьте ответ Роберта Найт! Использует С++ 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]
Ответ 2
Вы можете использовать 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 и выше. Если вам нужна переносимость для других компиляторов, отличных от С++ 11, вы можете использовать std::tr1::aligned_storage
и std::tr1::alignment_of
и заголовок <tr1/type_traits>
.
В приведенном выше коде AlignedStorage<T>::data
будет представлять собой тип POD (массив char [] в MSVC и GCC) подходящего выравнивания для T и размера T * N.
Ответ 3
Вы уверены, что MyType
- допустимая целая мощность?
__declspec( align( # ) ) declarator
# - значение выравнивания. Допустимыми значениями являются целые степени, равные двум из От 1 до 8192 (байт), таких как 2, 4, 8, 16, 32 или 64. декларатором являются данные которые вы объявляете как выровненные.
- align (С++)
Ответ 4
Как насчет alloca()? (Или, более конкретно, для MSVC2008, _ malloca())?
Выделяет память в стеке. Это версия _alloca с улучшениями безопасности, как описано в разделе "Улучшения безопасности" в CRT.
Поведение сопоставления не стандартизировано для всех компиляторов, но для этого...
Процедура _malloca возвращает указатель void на выделенное пространство, которое, как гарантируется, будет соответствующим образом выровнено для хранения любого типа объекта.
Ответ 5
Была та же проблема.
Вы можете смять макрос (oh horrors), который сочетает 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)];
Ответ 6
Вам не нужно выполнять никаких дополнительных "хаков", если вы хотите объявить выровненные данные в стеке. Компилятор позаботится об этом.
Если вы хотите, чтобы это был массив 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 будет литералом. Он должен быть числом, а не вызовом функции.