19

I would like to create a struct that has a certain alignment.

I would like to use the same struct definition for both GCC and VisualC++ compilers.

In VisualC++, one typically does this:

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

In GCC, one typically does this:

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

I could of course create the appropriate macros to make this work:

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

And thus be able to handle both cases transparently, but here I have to duplicate the alignment constant (32), which I'd like to avoid.

An alternative in GCC is to put the __attribute__ after the struct tag, as mentioned in the docs, like so:

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

And thus I could make this type of syntax work:

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

Does anyone have any better versions? Other ideas? I tried a little code searching, but didn't find anything too promising.


Update: Based on @John's comment, here's another version that could work (I haven't compiled it, but the docs indicate it's an OK idea)

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
Tomas Kubes
  • 23,880
  • 18
  • 111
  • 148
jwd
  • 10,837
  • 3
  • 43
  • 67
  • does `__attribute__` work on typedef? define `struct MyUnalignedStruct {...}` and have a macro that typedefs the struct with the required alignment. – John Oct 25 '11 at 21:08
  • @John: Yes, you can, but the placement rules are the same, and thus GCC and VisualC++ are still incompatible. You can do `typedef __declspec(...) struct ...` or you can do `typedef struct __attribute__(...) ...` - I'll add that to the list, though. – jwd Oct 25 '11 at 21:24
  • It's unfortunate that Microsoft hasn't added support for the newly standardized `alignas` keyword. – Ben Voigt Oct 25 '11 at 23:06

2 Answers2

33

I know this thread is quite old - however it is yet to be marked as answered and the solutions mentioned are not the easiest to use. The best way to solve this is to notice that MSVC allows the declspec to appear after the declarator. Here is my own implementation:

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

You can test this with the following code (notice the #pragma pack --> This is for 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;
}

Hope this helps...

Sebastian Cabot
  • 1,812
  • 14
  • 18
  • 1
    I'll take it. It's a nice variation on @John's version from the comments. And thanks for the thorough answer (: – jwd Oct 01 '12 at 18:09
  • Thank you for marking the answer. Notice that it's not a variation. the _ALIGNED macro can be used regardless of whether you use typedef or not – Sebastian Cabot Oct 01 '12 at 19:07
  • 3
    Good answer with one problem. The name _ALIGNED is reserved by the C++ standard. You are not allowed to define a macro with that name. See this question ( http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier ) for more details – Dale Wilson Jul 03 '14 at 17:53
  • 1
    Heads up: `__declspec(align(2 + 2))` is not supported by MSVC. – zwhconst Nov 08 '19 at 02:19
9

On Modern Compilers

The new versions of GCC (4.8.1) and VC++ (VS2013) supports the common syntax of having the attribute between struct and the identifier name. This is perhaps due to the new C++11 standard's introduction of the alignas keyword to do the same job the compilers' alignment attribute does; however, alignas can only make the alignment more stricter (wider) than the datatype's natural alignment, while the compiler directive can make it more lenient too. This is the reason I prefer compiler attributes over C++11-introduced alignas specifier (or alignof operator).

We can define something that works and has a common syntax across compilers:

#if defined(__GNUC__) || defined(__clang__)
#  define ALIGN(x) __attribute__ ((aligned(x)))
#elif defined(_MSC_VER)
#  define ALIGN(x) __declspec(align(x))
#else
#  error "Unknown compiler; can't define ALIGN"
#endif

#if defined(__GNUC__) || defined(__clang__)
#    define ALIGNOF(X) __alignof__(X)
#elif defined(_MSC_VER)
#    define ALIGNOF(X) __alignof(X)
#else
#  error "Unknown compiler; can't define ALIGNOF"
#endif

Example client code using the above

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

static_assert(ALIGNOF(MyStruct) == 32, "Error: MyStruct not on a 32-byte boundary!");
legends2k
  • 31,634
  • 25
  • 118
  • 222