38

Given a void * to some storage, how to check whether it points to properly aligned storage without any implementation defined behavior?

Of course we have std::align, but is there a more effective way to do this?

template <std::size_t alignment>
inline bool is_aligned(void * ptr) noexcept {
    std::size_t max = 1u;
    return std::align(alignment, 1u, ptr, max);
}

PS: I need to do this in a C++ standards-compatible fashion, without relying on any platform-specific (implementation defined) hacks.

PPS: I apologize for my (comprehension of) English, its not my native language.


EDIT (2018.08.24): Removed "effective" from the title, added even more wording to emphasize that I don't want any implementation defined or platform-specific behavior.

jotik
  • 17,044
  • 13
  • 58
  • 123
  • The important thing about pointers (in a case like this) is to remember that they are really only integer values corresponding to an address. It's the compilers treatment of them that makes them special. And since a pointer is just an integer you can use normal bitwise operators on them (with the proper casting of course, `std::intptr_t` is useful here). For example to check if a pointer is on an even address you could do `reinterpret_cast(some_pointer) & ~1`. – Some programmer dude Feb 07 '17 at 15:20
  • With that said, since `ptr` (in your example) is not itself a `constexpr` then neither can the result of any calculation using it be `constexpr`. – Some programmer dude Feb 07 '17 at 15:23
  • 2
    Also note that alignment is by definition platform specific and operating on numerical values returned from a reinterpret_cast such as suggested by @Some is inherently undefined or implementation defined behaviour. – rubenvb Feb 07 '17 at 15:24
  • 2
    Lastly, if you have allocated memory using `new` or `new[]` you can be certain that the memory should be well-aligned for the type you're working with. Perhaps you can enlighten us about the problem you are actually trying to solve by checking the alignment? Why do you need to check it? – Some programmer dude Feb 07 '17 at 15:27
  • 3
    @Someprogrammerdude This can be useful when one has a buffer with arbitrary raw data, and one needs to check whether parts of it can be accessed directly as some type `T` or whether one needs to use `std::memcpy` to copy the respective data from/to the buffer before/after accessing these as type `T`. – jotik Feb 07 '17 at 15:43
  • 3
    @Someprogrammerdude before C++17 that's only true for types with standard alignment, it's not true for something like `struct OveralignedInt { alignas(1024) int i; };` – Jonathan Wakely Feb 07 '17 at 16:07
  • 1
    @jotik It is UB to access arbitrary raw data (`char` buffer) as some `T` even when alignment requirements are met. You'll have to begin the lifetime of the object with placement `new` first. – yuri kilochek Feb 10 '17 at 22:00
  • @jotik: How would you have such a buffer at *compile time*? Compile time C++ does not allow for that kind of trickery. So why would your detection function need to be `constexpr`? – Nicol Bolas Feb 11 '17 at 01:42
  • 1
    @yurikilochek Even for trivially copyable types? – jotik Feb 11 '17 at 08:49
  • @NicolBolas I wasn't sure, and that's why I used the word *preferably*. – jotik Feb 11 '17 at 08:50
  • 1
    @jotik [yes, even for those](http://stackoverflow.com/questions/40873520/reinterpret-cast-creating-a-trivially-default-constructible-object) – yuri kilochek Feb 11 '17 at 13:27
  • @yurikilochek Even when its not a `char` buffer, but just storage allocated by, say, `::operator new(std::size_t)`? – jotik Feb 17 '17 at 08:49
  • @jotik yes, likewise for `malloc`. There is no object for you to access until you create it. – yuri kilochek Feb 17 '17 at 12:11
  • Possible duplicate of [How to determine if memory is aligned?](https://stackoverflow.com/a/28760180/608639) @Atlaste's answer is C++ and seems to answer your question. – jww Aug 24 '18 at 14:33
  • @jww Both of these rely on implementation-defined behavior. I edited my question to emphasize even more that I don't want to rely on implementation-defined behavior. – jotik Aug 24 '18 at 19:10

1 Answers1

31

If the remainder isn't zero when dividing the address with the desired alignment, then the address isn't aligned.

inline bool
is_aligned(const void * ptr, std::uintptr_t alignment) noexcept {
    auto iptr = reinterpret_cast<std::uintptr_t>(ptr);
    return !(iptr % alignment);
}

Ths can't be constexpr though, because of the cast.

Also, this relies on the implementation-defined fact that the conversion from pointer to integer must preserve the numeric representation of the address. As pointed out by the comments, that is not guaranteed by the standard, so this function is not necessarily portable to all platforms. That is also true, because it is optional for the implementation to provide std::uintptr_t.


I would expect this to only be needed when aligning for a type, so this might be more convenient:

template<class T>
bool
is_aligned(const void * ptr) noexcept {
    auto iptr = reinterpret_cast<std::uintptr_t>(ptr);
    return !(iptr % alignof(T));
}
Todd Lehman
  • 2,880
  • 1
  • 26
  • 32
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 3
    Any pointer may be converted to an unsigned integer at least the size of `uintptr_t`. However, the result is implementation-defined. You're assuming a common implementation is used and the memory model is flat, basically, which is very likely these days, but not guaranteed. – Arne Vogel Feb 07 '17 at 18:06
  • 3
    @ArneVogel yeah, seems like this is not guaranteed to be strictly portable to obscure platforms, where the conversion might change the alignment. – eerorika Feb 07 '17 at 18:52
  • You can make is_aligned constexpr like follows: `template inline constexpr bool is_aligned(const void *p) { return (reinterpret_cast(p) % alignof(T)) == 0; }` – Phil Armstrong Feb 10 '17 at 19:47
  • If you know you’re on a 2s complement machine, then `& (alignof(T)-1)` might be faster than `% alignof(T)`, but I’d hope that the compiler would spot that itself. – Phil Armstrong Feb 10 '17 at 19:50
  • 1
    Arne Vogel: the presence of uintptr_t on a platform is probably a hint that such a conversion is a reasonable thing to do however. – Phil Armstrong Feb 10 '17 at 20:35
  • 2
    @PhilArmstrong 2's complement is not used for unsigned numbers, it has no relevance :) But yes, any decent optimizer will be able to do that transformation. – eerorika Feb 10 '17 at 21:36
  • @PhilArmstrong - can you determine at compile-time whether `std::uintptr_t` exists, other than invoking a compiler error? E.g., via a macro or other such thing, in a way such that you can compile alternate code-paths depending on its existence. – BeeOnRope Feb 10 '17 at 21:57
  • 2
    @PhilArmstrong I tested your function, and also tested adding constexpr to my template. It works, to my surprise. I'm confused because I don't understand why reinterpret_cast can be used in a constexpr template, but not in a regular constexpr function. Can you explain / refer to the standard? – eerorika Feb 10 '17 at 22:00
  • 1
    Point on the 2s complement! Regarding constexpr & templates. That’s a very good question! Might just be a compiler bug? – Phil Armstrong Feb 10 '17 at 23:06
  • clang-4.0 compiles my templated version happily as well, so it’s a fairly widespread error! (g++ 6.3 still accepts reinterpret_cast in ordinary constexpr functions by the looks of things, but clang++4 rejects them as the standard requires.) – Phil Armstrong Feb 10 '17 at 23:29
  • @BeeOnRope You can use SFINAE to do it, via some icky testing for the existence of a destructor for uintptr_t: http://stackoverflow.com/questions/10711952/how-to-detect-existence-of-a-class-using-sfinae – Phil Armstrong Feb 10 '17 at 23:36
  • 2
    @PhilArmstrong : Constexpr function templates for which no template argument will satisfy all `constexpr` requirements are ill-formed, no diagnostic required. This is what both you and @user2079303 are running into. – ildjarn Feb 13 '17 at 16:44
  • @ildjarn If it’s ill formed, then why isn’t the compiler complaining about it? SFINAE doesn’t apply if you’re actually instantiating the template in question! – Phil Armstrong Feb 15 '17 at 14:54
  • @ildjarn some rules are not diagnosable. Standard doesn't require a diagnostic when such rules are violated. This is one of those rules. – eerorika Feb 15 '17 at 15:05
  • @PhilArmstrong : That's what 'no diagnostic required' means. See another of user2079303's answers, [Ill-Formed, No Diagnostic Required (NDR): ConstExpr Function Throw in C++14](http://stackoverflow.com/a/41961562/636019) – ildjarn Feb 15 '17 at 23:44
  • 1
    K. I wasn’t aware that "ill-formed, no diagnostic required" was actual standard-ese. (Also, Ugh. What a horrible trap to set for programmers to walk into.) – Phil Armstrong Feb 17 '17 at 11:41
  • 1
    @ArneVogel to quote the standard (draft): `typedef unsigned integer type uintptr_t; // optional` – eerorika May 24 '17 at 15:57
  • Oops, you're right, my bad! The only "official" guarantee (inherited from C99) is still safe round-trip conversion, however: "The following type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer:" (7.18.1.4) – Arne Vogel May 24 '17 at 16:43