Кроссплатформенный макрос ALIGN (x)? - PullRequest
16 голосов
/ 26 октября 2011

Я хотел бы создать struct, который имеет определенное выравнивание.

Я хотел бы использовать одно и то же определение структуры для компиляторов GCC и VisualC ++.

В VisualC ++ обычно это делают:

__declspec(align(32))
struct MyStruct
{
// ...
};

В GCC обычно это делают:

struct MyStruct
{
// ...
} __attribute__ ((aligned (32)));

Конечно, я мог бы создать соответствующие макросы для этой работы:

BEGIN_ALIGNED_STRUCT(32)
struct
{
// ...
}
END_ALIGNED_STRUCT(32)
;

И, таким образом, я могу прозрачно обрабатывать оба случая, но здесь я должен дублировать константу выравнивания (32), которой я бы хотел избежать.

Альтернатива в GCC - поставить __attribute__ после тега struct, как указано в docs , примерно так:

struct __attribute__ ((aligned (32))) MyStruct
{
// ...
};

И, таким образом, я мог заставить этот тип синтаксиса работать:

ALIGNED_STRUCT(32) MyStruct
{
// ...
};

У кого-нибудь есть лучшие версии? Другие идеи? Я попытался немного поискать код, но не нашел ничего слишком многообещающего.


Обновление: На основании комментария @ Джона, вот еще одна версия, которая может работать (я не скомпилировал ее, но документы показывают, что это хорошая идея)

struct MyStruct_Unaligned
{
// ...
};

TYPEDEF_ALIGNED(32, MyStruct_Unaligned, MyStruct);

// Would expand to one of:
// 
// typedef __declspec(align(32)) MyStruct_Unaligned MyStruct;
//
// typedef struct __attribute__ ((aligned (32))) MyStruct_Unaligned MyStruct

Ответы [ 2 ]

25 голосов
/ 29 сентября 2012

Я знаю, что эта ветка довольно старая, однако она еще не помечена как отвеченная, и упомянутые решения не самые простые в использовании. Лучший способ решить эту проблему - заметить, что MSVC позволяет declspec появляться после объявления. Вот моя собственная реализация:

#if defined(_MSC_VER)
#define ALIGNED_(x) __declspec(align(x))
#else
#if defined(__GNUC__)
#define ALIGNED_(x) __attribute__ ((aligned(x)))
#endif
#endif

#define _ALIGNED_TYPE(t,x) typedef t ALIGNED_(x)

/*SOME USAGE SAMPLES*/

ALIGNED_TYPE_(double, 16) aligned_double_t;

ALIGNED_TYPE_(struct, CACHE_LINE) tagALIGNEDSTRUCT
{
    /*STRUCT MEMBERS GO HERE*/
}aligned_struct_t;

ALIGNED_TYPE_(union, CACHE_LINE) tagALIGNEDUNION
{
    /*UNION MEMBERS GO HERE*/

}aligned_union_t;

Вы можете проверить это с помощью следующего кода (обратите внимание на пакет #pragma -> Это для MSVC)

#if defined(_MSC_VER)
#define ALIGNED_(x) __declspec(align(x))
#else
#if defined(__GNUC__)
#define ALIGNED_(x) __attribute__ ((aligned(x)))
#endif
#endif

#define ALIGNED_TYPE_(t,x) typedef t ALIGNED_(x)

#pragma pack(1)
typedef struct tagSTRUCTPACKED
{
    int alignedInt;
    double alignedDouble;
    char alignedChar;
}struct_packed_t;
#pragma pack()

typedef struct tagSTRUCTNOALIGN
{
    int alignedInt;
    double alignedDouble;
    char alignedChar;
}struct_no_align_t;

typedef struct ALIGNED_(64) tagSTRUCTALIGNED64
{
    int alignedInt;
    double alignedDouble;
    char alignedChar;
}struct_aligned_64_t;


typedef struct tagSTRUCTWITHALIGNEDMEMBERS
{
    int ALIGNED_(8) alignedInt;
    double ALIGNED_(16) alignedDouble;
    char ALIGNED_(2) alignedChar;
}struct_with_aligned_members_t;

int main(int argc, char **argv)
{
    int i,j;
    struct_packed_t _packed;
    struct_no_align_t _noalign;
    struct_aligned_64_t _aligned64;
    struct_with_aligned_members_t _alignedmembers;

    char* names[] = {"_packed","_noalign","_aligned64","_alignedmembers"};
    char* ptrs[] = {(char*)&_packed,(char*)&_noalign,(char*)&_aligned64,(char*)&_alignedmembers};
    size_t sizes[] = {sizeof(_packed),sizeof(_noalign),sizeof(_aligned64),sizeof(_alignedmembers)};
    size_t alignments[] = {2,4,8,16,32,64};
    int alcount = sizeof(alignments)/sizeof(size_t);

    for(i = 0; i < 4; i++)
    {
        printf("Addrof %s: %x\n", names[i], ptrs[i]);
        printf("Sizeof %s: %d\n", names[i], sizes[i]);
        for(j = 0; j < alcount; j++)
            printf("Is %s aligned on %d bytes? %s\n", 
                names[i], 
                alignments[j], 
                ((size_t)ptrs[i])%alignments[j] == 0 ? "YES" : "NO");
    }

    for(j = 0; j < alcount; j++)
    {
            printf("Is _alignedmember.alignedInt aligned on %d bytes? %s\n", 
                    alignments[j], 
                    ((size_t)&_alignedmembers.alignedInt)%alignments[j] == 0 ? "YES" : "NO");
            printf("Is _alignedmember.alignedDouble aligned on %d bytes? %s\n", 
                    alignments[j], 
                    ((size_t)&_alignedmembers.alignedDouble)%alignments[j] == 0 ? "YES" : "NO");
            printf("Is _alignedmember.alignedChar aligned on %d bytes? %s\n", 
                    alignments[j], 
                    ((size_t)&_alignedmembers.alignedChar)%alignments[j] == 0 ? "YES" : "NO");
    }

    return 0;
}

Надеюсь, это поможет ...

5 голосов
/ 13 августа 2015

На современных компиляторах

Новые версии GCC (4.8.1) и VC ++ (VS2013) поддерживают общий синтаксис наличия атрибута между struct и именем идентификатора.Возможно, это связано с введением нового стандарта C ++ 11 ключевого слова alignas для выполнения той же работы, что и атрибут выравнивания компиляторов;однако alignas может сделать выравнивание только более строгим, чем естественное выравнивание типа данных, в то время как директива компилятора также может сделать его более мягким.

В сторону: В VS2013 alignof и stdalign.h не поддерживается;хотя VS2015 поддерживает alignof и alignas, он все еще не имеет заголовка, предписанного стандартом C ++ cstdalign (или его аналогом C);поэтому доступность следует проверять вручную на основе версии цепочки инструментов.Возможно, это связано с плохой поддержкой языка C Visual Studio.GCC, OTOH, имеет все это;header, макросы (__alignas_is_defined и __alignof_is_defined) и ключевые слова (alignas и alignof).

С помощью этой информации мы можем определить что-то, что работает и имеет общий синтаксис для компиляторов:

#ifdef _MSC_VER
#    if (_MSC_VER >= 1800)
#        define __alignas_is_defined 1
#    endif
#    if (_MSC_VER >= 1900)
#        define __alignof_is_defined 1
#    endif
#else
#    include <cstdalign>   // __alignas/of_is_defined directly from the implementation
#endif

#ifdef __alignas_is_defined
#    define ALIGN(X) alignas(X)
#else
#    pragma message("C++11 alignas unsupported :( Falling back to compiler attributes")
#    ifdef __GNUG__
#        define ALIGN(X) __attribute__ ((aligned(X)))
#    elif defined(_MSC_VER)
#        define ALIGN(X) __declspec(align(X))
#    else
#        error Unknown compiler, unknown alignment attribute!
#    endif
#endif

#ifdef __alignof_is_defined
#    define ALIGNOF(X) alignof(x)
#else
#    pragma message("C++11 alignof unsupported :( Falling back to compiler attributes")
#    ifdef __GNUG__
#        define ALIGNOF(X) __alignof__ (X)
#    elif defined(_MSC_VER)
#        define ALIGNOF(X) __alignof(X)
#    else
#        error Unknown compiler, unknown alignment attribute!
#    endif
#endif

Пример кода клиента с использованием приведенного выше

struct ALIGN(32) MyStruct
{
    ...
};

static_assert(ALIGNOF(MyStruct) == 32, "Error: MyStruct not on a 32-byte boundary!");
...