Reputation: 53
I have something like this:
void* a[] = {(void*)"Hello",(void*)1};
cout << (char*)a[0] << endl;
cout << (int)(int64_t)((int*)a[1]) << endl;
How I can simplify line (int)(int64_t)((int*)a[1])
Is it possible to change the void * into an int easier?
Upvotes: 0
Views: 265
Reputation: 364180
Does it have to be an array of void*
? It looks like you should just use a union.
#include <cstdint>
#include <iostream>
union pointer_or_integer_t {
void *vp;
uintptr_t u;
intptr_t i;
};
static_assert(sizeof(void *) == sizeof(pointer_or_integer_t),
"intptr_t or uintptr_t are larger than the size as a pointer");
int main() {
pointer_or_integer_t a[] = {(void*)"Hello", {.i = 1}};
std::cout << (const char*)a[0].vp << '\n';
std::cout << a[1].i << '\n';
}
(Don't use std::endl
unless you actually want to force a flush.)
This compiles to code that does what we want, with no extra overhead on the Godbolt compiler explorer.
Note that a {.i = 1}
designated-initializer in C++ is currently a GNU extension, but will be part of C++20. g++8.2 -pedantic
warns:
<source>:15:50: warning: C++ designated initializers only available with -std=c++2a or -std=gnu++2a [-Wpedantic]
pointer_or_integer_t a[] = {(void*)"Hello", {.i = 1}};
^
You could also maybe use a union of void []
and uintptr_t[]
, but that only works easily if the size is known. Making each array element its own union works better.
I think it's still UB to type-pun the array-of-union to array-of-void*
with a cast (instead of memcpy
), even if you use asserts to check that it has the same layout.
But if you have an interface that only lets you pass a void **
, you can cast that back to pointer_or_integer_t **
in the receiving function. There's no UB at all if no code ever actually indexes the void **
.
There's usually no problem in practice if something does index into the object as a void **
, because your union has the same object-representation as an array of void*
.
Upvotes: 0
Reputation: 275385
void* a[] = {static_cast<void*>(const_cast<char*>("Hello")),reinterpret_cast<void*>(static_cast<omyptr_t>(1))};
std::cout << static_cast<char*>(a[0]) << std::endl;
std::cout << static_cast<int>(reinterpret_cast<intptr_t>(a[1])) << std::endl;
I think that is all defined behaviour.
Always cast back to the exact same when casting from void*
. Avoid using raw C casts, as it can be static, const or reinterpret casts, and doing the wrong one can really break things.
Consider using std::any
or boost::any
instead of raw void pointers; or variants if you know a fixed set of types to store.
Upvotes: 2