idiot
idiot

Reputation: 53

void* array convert element to another type

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

Answers (2)

Peter Cordes
Peter Cordes

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

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

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

Related Questions