Reputation:
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_cast
s 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
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
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