0

I have a uint32_t (and in the future it might become uint64_t) variable. There is a function in one of the libraries that I use that allows passing a void pointer to it. How can I pass a uint32_t to this void pointer:

uint32_t myEntity = 1242242;
// not a pointer; so, error about invalid conversion
actor->userData = static_cast<void *>(myEntity);

I can't use a pointer to this primitive type because the value can be coming from an argument or similar and since it is a uint, I don't want to use references for it.

Is there some way that I can set this value into a void pointer; so that, I can then retrieve it the same way without creating a value in the heap for it to work properly. Otherwise, I will need to manage the destruction of the objects in order to not cause memory leaks.

Gasim
  • 7,615
  • 14
  • 64
  • 131
  • do you mean you want to pass a `void *` pointing to ` myEntity` or coerce `1242242` into being a `void*` pointer – pm100 Feb 23 '22 at 23:39
  • Alternatively, you could use the `reinterpret_cast`. However, your code does already what you want: It does not pass a pointer to your variable but instead casts the variable as if the address were 1242242. Reading the variable later requires casting the userData back to a uint32_t (or uint64_t in the future). – J.P.S. Feb 23 '22 at 23:39
  • @pm100 I want to coerce `1242242` into being a `void*` pointer. – Gasim Feb 23 '22 at 23:52
  • You have to be careful, though. If you pass it as a pointer and then the variable pointed to goes out of scope, the pointer is no longer valid---or do I misunderstand? Are you getting a memory address as a uint32? – dmedine Feb 23 '22 at 23:52
  • @J.P.S. since my value is an unsigned integer, I am technically treating it as an address in memory for the void pointer to take, then using that "memory address" as a value, which will return back the uint, right? – Gasim Feb 23 '22 at 23:53
  • 2
    @Gasim "*I want to coerce `1242242` into being a `void*` pointer*" - then you need to use `reinterpret_cast` for that, not `static_cast`, eg: `actor->userData = reinterpret_cast(myEntity);` ... `uint32_t myEntity = reinterpret_cast(actor->userData);` – Remy Lebeau Feb 23 '22 at 23:54
  • @dmedine That's what I also want to avoid. I want to basically pass in a value there and just use it. The pointer itself should be the value, the value of the pointer is useless to me. – Gasim Feb 23 '22 at 23:54
  • 1
    @Gasim that's right. But it would even work with a signed integer or a double. It's imple a 64 bit mask which is reinterpreted and then reinterpreted back, If no bit changes, then no value changes. BUT, as I already mentioned: you need the `reinterpret_cast` @Remy mentioned as well ;) – J.P.S. Feb 23 '22 at 23:56
  • I don't think that is a good use of pointers. In my experience, `void *` type is used in C++ when you want to pass a function as a callback, usually to some C code. I wouldn't ever attempt to pun a number to a pointer value. I also wouldn't use a library that requires me to do this :/ – dmedine Feb 23 '22 at 23:57
  • Is the data being passed by pointer being stored by the called function so that it outlives the function call? If this is a one-off "pass in data, call has a callback that receives data, data no longer needed after", there's no issue passing the address of a stack variable (like a parameter) that outlives the call, and it's a lot more normal for the reader than this byte-play. – chris Feb 23 '22 at 23:58
  • @dmedine You are right in principle. However, this is common practice in a lot of APIs. – J.P.S. Feb 23 '22 at 23:59
  • @J.P.S., I see this often in the case of interop, but usually there there is some decoration to make the marshalling look more 'official'. I have never encountered this in a C/C++ API. Can you point to some examples (this is verging on an off topic discussion...) – dmedine Feb 24 '22 at 00:02
  • 1
    @chris Unfortunately, this ID that I am storing can stay there for a while. – Gasim Feb 24 '22 at 00:02
  • @dmedine I have used the user pointer as a in GLFW window library to set my class instance but I was still dealing with pointers there. I am currently using a PhysX library and it allows me to store any user data in there that I can later access. I want to store ID of the entity that the Actor/Object in PhysX library refers to; so, I can get the necessary components and other information after the physics simulation is done. Otherwise, I need to create a hashmap of all the actors and their corresponding entities IDs, which is another data structure that I have to manage. – Gasim Feb 24 '22 at 00:04
  • @dmedine Yeah, sure. Take the WinAPI as probably most popular example. It has been ages (over 10 years) that I coded for windows, but I remember that this was common practice there. You can also see it in some places in the ROS API. See for example [here](https://stackoverflow.com/questions/117792/best-method-for-storing-this-pointer-for-use-in-wndproc) – J.P.S. Feb 24 '22 at 00:05
  • 1
    @Gasim "*I want to store ID of the entity that the Actor/Object in PhysX library refers to*" - why not store a pointer to the actual entity itself? – Remy Lebeau Feb 24 '22 at 00:06
  • I have used WinAPI a lot. It is indeed full of signatures with `void*` and `void**` as arguments, but in my experience these are always actually pointers, and in the call reverted to a method or data structure that gets dealt with appropriately, not values typecast as pointers then cast back as values. To me this is extremely unreadable. It would be better (although more code) to typedef a structure with a uint32_t as its only member and pass a pointer to that... – dmedine Feb 24 '22 at 00:09

1 Answers1

1

For what you are attempting, you need to use reinterpret_cast instead of static_cast, eg:

uint32_t myEntity = 1242242;
actor->userData = reinterpret_cast<void*>(myEntity);
uint32_t myEntity = reinterpret_cast<uint32_t>(actor->userData);

Since you mention that the value may be changed to a uint64_t in the future, just be aware that you will have to compile your code into a 64bit executable in that case (if you are not already), otherwise void* won't be large enough values that exceed the highest uint32_t value.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770