1

I know C++11 provides alignof, alignas and align, but in this case I want to check an input buffer that is already allocated.

I know also that C provides uintptr_t to precisely fit a pointer type in a conversion to integer (and then checking alignment would be easy), but this data type is not guaranteed to be there in C++/C++11.

The question is answered here for C. It seems that a conversion to any integer would be ok in this case, but in C++ I get a "loses precision" warning.

So, now I look at Converting a pointer into an integer, but there I find an abundant use of uintptr_t, which is not guaranteed to be there.

So, what is the best way to check if an input pointer is aligned in C++/C++11?

(Note: After all this research and reasoning I came up with a solution, but I am looking forward to other proposals!)

Antonio
  • 19,451
  • 13
  • 99
  • 197
  • Precision is lost when converting from a 64 bits pointer to an int. Use a long long int. –  Mar 16 '18 at 20:33
  • 3
    @YvesDaoust: Peer review functionality is lost when writing an answer (or part of an answer) in the comments section. Use the answer section, or leave the answering to someone else. And no, don't just randomly guess at type widths - use the proper type for the job..... See? I can't downvote your comment to denote it as incorrect for other visitors :(. – Lightness Races in Orbit Mar 16 '18 at 21:22

2 Answers2

3

uintptr_t is the right type to inspect the numeric value within a pointer. If that doesn't exist, it means that there is no integral type big enough for the whole pointer.

However, alignment only affects the low bits, so it's not actually necessary to store the entire value. size_t should always be suitable for capturing the bits related to alignment. (In particular, this is the result type of alignof so if it isn't sufficient, the language's own alignment logic will break)

From the Standard, section [basic.align]:

Alignments are represented as values of the type std::size_t. Valid alignments include only those values returned by an alignof expression for the fundamental types plus an additional implementation-defined set of values, which may be empty. Every alignment value shall be a non-negative integral power of two.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • How can `uintmax_t` not be big enough to old the numeric value of a pointer? Besides, I fail to see the consequentiality "if `uintptr_t` doesn't exist, than there is no integral type big enough for the whole pointer": that's not what the standard says as a rule to define or not `uintptr_t` (it's simply optional). – Antonio Mar 16 '18 at 21:55
  • Also, what you suggest (passing by `size_t`) and stating that "anyway we need the least significant bytes" does not fit together, because if you use a too small type you will get a "loses precision" warning. So, since we need the least significant bytes, how would you do the conversion for example from `void *` to `uint8_t`? (since we need the least significant bits anyway) – Antonio Mar 16 '18 at 22:08
  • @Antonio: `reinterpret_cast(pointer_to_test)` and it may be necessary to locally disable the warning. Also if the warning for integer narrowing says "loses precision" that's a compiler bug, it ought to say "loses range". – Ben Voigt Mar 16 '18 at 22:26
  • Different compiler output (using `unsigned int`, `size_t` would happen to be big enough): [Clang](http://rextester.com/HFKT78284): `loses information`; [g++](http://coliru.stacked-crooked.com/a/cc9019b10cedb2dd): `loses precision`. Visual Studio: `warning C4311: 'reinterpret_cast': pointer truncation` and `warning C4302: 'reinterpret_cast': truncation`. Pretty tough to silence in a multi-platform project. – Antonio Mar 16 '18 at 22:50
  • @Antonio: I'll be happy to vote for the bug reports you file with the compiler vendors. `size_t` is the correct type for storing alignment information according to the C++ standard. I'll add the citation. – Ben Voigt Mar 18 '18 at 16:17
0

This is the solution I am using right now with uintmax_t

#include <cstdint>

template<typename T>
bool isAlignedAs(void* in) {
  return !(reinterpret_cast<uintmax_t> (in) % alignof(T));
}

Tested here.


To confirm 100% that there won't be lost precision warning generated, one could add anywhere in the code an assertion:

static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
              "No suitable integer type for conversion from pointer type");
Antonio
  • 19,451
  • 13
  • 99
  • 197
  • `uintmax_t` is unnecessarily large and could be very slow (consider a 256-bit integer on today's CPUs -- carries and such would need to be performed by software emulation) – Ben Voigt Mar 16 '18 at 21:25
  • @BenVoigt No doubt the conversion operation will be optimized by the compiler (it's probably no op in fact). That's not a worry point. – Antonio Mar 16 '18 at 22:08