Query the alignment of a specific variable
Asked Answered
G

3

37

C++11 introduced the alignas specifier to specify the alignment of a variable, and the alignof operator to query the default alignment of a type. However, I don't see any way to get the alignment of a specific variable. Let's take the following trivial example:

alignas(16) float* array;

Here is what we can do about it:

  • alignof(float*) returns 8, which is obviously not what we want.
  • alignof(array) returns 16, which is exactly what we want, but that's a compiler extension; alignof as specified by the standard can't be used on a specific variable.
  • alignof(decltype(array)) returns 8, which was quite expected but not what we want.
  • std::alignment_of is implemented in terms of alignof, so it doesn't help much.

I would like a mechanism to confirm that the specific variable array is aligned on a 16 byte boundary. Is there anything in the standard to perform such a query?

Ganley answered 25/3, 2016 at 9:5 Comment(10)
alignof doesn't need to provide that - it returns a minimum contract at compile time - You want a value at run time. A specific variable might very well be aligned to a page boundary, megabyte-boundaries, or whatever at run time (i.e. much "better" than promised or asked for). Take the address of the variable and check that it is divisible by your wanted alignment value evenly.Kravits
@Kravits Right, I forgot to specify it, but I kind of half-expected alignof to turn into a runtime thingy when used on a variable name (I guess this is what the compiler extension does). I didn't expect everything to be known at compile-time.Ganley
I can't quite see whether something like #define ALIGNED8(x) ((&(x) && 0x7) == 0) doesn't fit your purpose or why the standard should have something that duplicates this simple construct.Kravits
Do you want to know that actual alignment or the minimal alignment that is guaranteed for a certain variable?Arnett
@Arnett The obvious use case: known whether the float array is suitably aligned to be used with SSE.Ganley
@Morwenn: Maybe I wasn't clear. Its the actual vs. guaranteed part that is important. I'm asking, because the former is a runtime property, that can (on most platforms) simply be determined as shown in the answers or by tofro. The latter however would be a compiletime property and I don't know of any way to retrieve that.Arnett
@Kravits maybe you meant &, but bitwise-and cannot be used on a pointerHeaven
@Arnett I... guess I'm interested in the latter? I mean, while we need the array of float to be 16 byte aligned, an array of float that is 32 byte aligned is also suitable SSE if I'm not mistken.Ganley
@M.M.: The example was not intended as an answer, but rather to deliberately demonstrate simplicity of a concept. reinterpret_cast is a bit long for a comment. And the && is actually a typo, right.Kravits
@tofro: Because not all pointers are simply memory addresses. I have used a machine where that would completely not work (Prime mini-computer. Word addressed, bit offset in a trailing extension word - but bit offset could only be 0 or 8.)Tocci
S
10

You can try with something like:

bool is_aligned(const volatile void *p, std::size_t n)
{
  return reinterpret_cast<std::uintptr_t>(p) % n == 0;
}

assert(is_aligned(array, 16));

The above assumes a flat address space and that arithmetic on uintptr_t is equivalent to arithmetic on char *.

While these conditions prevail for the majority of modern platforms, neither of which is required by the standard.

It's entirely possible for an implementation to perform any transformation when casting void * to uintptr_t as long the transformation can be reversed when casting back from uintptr_t to void * (see What is uintptr_t data type).

Further details in N4201 (it proposes, among other things, the is_aligned() operation).


EDIT

is volatile necessary here?

It allows something like:

alignas(16) volatile float a;

assert(is_aligned(&a, 16));

Without volatile you get the error

no known conversion from 'volatile float *' to 'const void *' for 1st argument

Further references:

Scientism answered 25/3, 2016 at 10:17 Comment(4)
I can see why a standardized function would be useful if it needs those guarantees. N4201 has been rejected though.Ganley
@manlio, is volatile necessary here? And if so, shouldn't it be const void* volatile?Hiramhirasuna
@Hiramhirasuna I've added some references to the answerScientism
The cast to uintptr_t doesn't have to do any transform for the arithmetic not to work. Consider the old 8086 address model where a 32-bit address consisted of a 16 bit segment pointer and a 16 bit offset, and the actual physical address was segment<<4 + offset.Tocci
C
7

This is currently handled by EWG 98. I submitted a paper on this:

The alignas specifier is applicable to objects, affecting their alignment requirement but not their type. It is therefore currently not possible to determine an object's actual alignment requirement. This paper proposes to permit application of alignof to objects and references.

The best you can do at this point in time is to define a separate variable holding the variable's alignment.

Citation answered 25/3, 2016 at 15:2 Comment(1)
I'm glad to see that other people thought about it and started working on it :)Ganley
F
3

You could try this:

template<size_t size, typename T>
constexpr bool IsAlignedAs(const T& v)
{
    return (reinterpret_cast<const size_t>(&v) % size) == 0;
}

std::cout << IsAlignedAs<16>(array) << std::endl;
std::cout << IsAlignedAs<32>(array) << std::endl;
Feaze answered 25/3, 2016 at 10:17 Comment(3)
By the way, you could use static_assert(IsAlignedAs<16>(array), "Array not aligned to 16"); for your array, but you cannot use static_assert(IsAlignedAs<32>(array), "Array not aligned to 32"); since it is undecided at compile-time whether or not this is true.Feaze
Not a constant expression for clang.Lyrist
Indeed it seems gcc specific. Checking the alignment of an address at compile-time (before linking it to its final location) already seemed a bit premature...Feaze

© 2022 - 2024 — McMap. All rights reserved.