user10043112
user10043112

Reputation:

replace reinterpret_cast by unions in constexpr - good idea?

I am using this code to convert a pointer to a size_t, it is used in a ::std::hash function that should hash a given pointer at compile time, and since reinterpret_casts are not allowed in constexpr, I came up with the following solution, it works as expected but I want to know whether this can be considered a good idea. It would also be great if you could give me a few pointers on how portable this piece of code actually is.

union {
   size_t datr;
   void* pointer;
} dummy{.pointer = (void*) somePointer};
size_t pointerAsAsize_t = dummy.datr; // how portable is this?

Is there any better solution - as stated above, I want to create a ::std::hash<some_pointer*> that runs at compile time.

Upvotes: 0

Views: 505

Answers (2)

Nicol Bolas
Nicol Bolas

Reputation: 473976

If you attempt to perform that in a constant expression context (ie: when the compiler is forced to call your code at compile time, such as the expression leading to an array size or a template argument), you will find that it will not compile. Constexpr execution requires that anything which would provoke UB at compile time will instead be ill-formed. And since your code provokes UB (by accessing a union member which is not active), it will fail to compile.

All three major compilers will fail on this in a constant-expression context. MSVC (surprisingly) gives the best, most direct error message:

(13): error C2131: expression did not evaluate to a constant
(9): note: failure was caused by accessing a non-active member of a union
(9): note: see usage of 'foo::Foo::integer'

Your code probably "works as expected" only because you're calling it when the compiler doesn't have to execute it at compile-time. Given GCC/Clang's errors, if you used it as the array size for an array on the stack, it probably invoked GCC/Clang's variable-length array language extension. If you turned that off, your code would likely not compile. My example made it a global array, which doesn't allow VLAs.


Is there any better solution

Nope. Even C++20's bit_cast will not be constexpr if you provide a pointer (or a type containing pointers) as either the source or destination. Compile-time pointers are real things, not just numbers that represent addresses; converting them to/from integers is simply not a reasonable activity at compile-time.

Compile-time pointers have to point to things that exist at compile-time. There is no way to know where they will point at runtime. So the idea of hashing the compile-time value of a pointer (even a pointer to a static/global object) is just doomed from the start.

Upvotes: 4

Dmitry Gordon
Dmitry Gordon

Reputation: 2324

I think on most of the plaftorms this code will work, however formally it can not. size_t is an unsigned type to hold the result sizeof(xxx) constructrion. There is no guarantee that is is big enough to store a pointer. You can add static_assert(sizeof(size_t) == sizeof(void*),"") or use std::intptr_t if it's available in your library.

Upvotes: -2

Related Questions