Search code examples
c++c++11templatesvisual-c++memory-alignment

When is the __unaligned specifier used with pointers?


Background

I'm currently working with variable length arrays with their length stored in the first two bytes of the element. Since the arrays are not padded, individual elements may be unaligned. Only the first element is guaranteed to be aligned, and the whole thing is double null terminated. These arrays are used by an API that I need access to and cannot alter.

I'm writing a const_iterator that takes an aligned pointer and can iterate over the unaligned elements. The API for these arrays specifies '__unaligned' for pointers to elements, so I'm mostly trying to confirm or deny my understanding of applying the __unaligned specifier.

The Question

Simply: when should the __unaligned specifier be used when dealing with pointers to potentially unaligned data?

Let us say that I have

Foo *foo = pointerToFirstElement(); //aligned
auto next_ptr = reinterpret_cast<unsigned char *>(foo);
next_ptr += bytesToNextElement(foo);
//reinterpret_cast here to retrieve the next element

Do I then need to check if the new address is aligned, and if not, reinterpret_cast<Foo __unaligned *>(next_ptr) to access the next element?

Further, can I use __unaligned as part of template argument deduction in a manner similar to const, like so?

template <class T, class = std::enable_if<std::is_pointer<T>::value>>
struct is_pointer_to_aligned_data : std::true_type {};

template <class T>
struct is_pointer_to_aligned_data<T __unaligned *> : std::false_type {};

Solution

  • Simply: when should the __unaligned specifier be used when dealing with pointers to potentially unaligned data?

    • In short, when the platform you are using demands it, e.g. the Windows API on x86-64. It's a legacy throwback to Itanium and not a concern on x86-32. Some other processors also have issues with unaligned data (e.g. MIPS).

    • Specifically, the __unaligned specifier simply means data may not be on an aligned boundary. It has no meaning outside of a pointer type.

    • On 32 bit x86 platforms, Windows itself simply declares #define __unaligned.

    Do I then need to check if the new address is aligned, and if not, reinterpret_cast(next_ptr) to access the next element?

    • Given the above, no. Adding the __unaligned specifier is useful to access functions that take arguments that are pointers to unaligned data, such as ILFindLastID.

    • Followup: How do you remove the __unaligned specifier when unneeded? Use const_cast or do like Windows: #define __unaligned before including a header. Use preprocessor checks that you are on x86-64 or another platform where this is safe.

    Lastly,

    Further, can I use __unaligned as part of template argument deduction in a manner similar to const, like so?

    Yes, but like this:

    template< class T > struct is_aligned_helper : std::true_type {};
    template< class T > struct is_aligned_helper<__unaligned T> : std::false_type {};
    template< class T > struct is_aligned : is_aligned_helper<typename std::remove_cv_t<std::remove_pointer_t<T>>> {};
    

    You need to remove the pointer first to check the type of the pointed to data. Then remove any const or volatile to get the bare type + __unaligned (if present).