views:

4372

answers:

10

I have a function with prototype void* myFcn(void* arg) which is used as the starting point for a pthread. I need to convert the argument to an int for later use:

int x = (int)arg;

The compiler (GCC version 4.2.4) returns the error:

file.cpp:233: error: cast from 'void*' to 'int' loses precision

What is the proper way to cast this?

+1  A: 

The proper way is to cast it to another pointer type. Converting a void* to an int is non-portable way that may work or may not! If you need to keep the returned address, just keep it as void*.

AraK
The point is (probably) that the value passed to the thread is an integer value, not really a 'void *'.
Jonathan Leffler
Thanks Jonathan, I was thinking about my answer in another thread: http://stackoverflow.com/questions/1593580/c-how-to-get-the-address-stored-in-a-void-pointer/1593588#1593588
AraK
AraK is correct, passing integers a pointers are not necessarily interchangeable. He should pass the address of the integer, the thread should get that address, `static_cast` it to an `int*`, then get the value. This must be done before the integer goes out of scope.
GMan
+1  A: 

Instead of:

int x = (int)arg;

use:

int x = (long)arg;

On most platforms pointers and longs are the same size, but ints and pointers often are not the same size on 64bit platforms. If you convert (void*) to (long) no precision is lost, then by assigning the (long) to an (int), it properly truncates the number to fit.

Joshua D. Boyd
Not valid on Windows 64 - long is still 32-bit but pointers are 64-bit.
Jonathan Leffler
That's not a good idea if the original object (that was cast to void*) was an integer. If the sizes are different then endianess comes into play.
Martin York
@Martin York: No, it doesn't depend on endiannness. The mapping in pointer<->integer casts is implementation defined, but the intent was that if the pointer type is large enough and isn't forcefully aligned (`void*` doesn't) then round-trip cast integer-to-pointer-to-integer should produce the original value. Same for pointer-to-integer-to-pointer round trip.
AndreyT
+9  A: 

You can cast it to an intptr_t type. It's an int type guaranteed to be big enough to contain a pointer.

Ferruccio
Don't forget to `#include <stdint.h>` to get the `intptr_t` typedef.
Michael Burr
`#include <inttypes.h>`. It includes stdint.h .
Yktula
A: 

Well it does this because you are converting a 64 bits pointer to an 32 bits integer so you loose information.

You can use a 64 bits integer instead howerver I usually use a function with the right prototype and I cast the function type : eg.

void thread_func(int arg){
...
}

and I create the thread like this :

pthread_create(&tid, NULL, (void*(*)(void*))thread_func, (void*)arg);
Ben
If the function had the correct signature you would not need to cast it explicitly.
Martin York
But then you need to cast your arguments inside your thread function which is quite unsafe ... cf. this question
Ben
You think by casting it here that you are avoiding the problem! You are just making it bigger by tricking the compiler and the ABI.
Martin York
Casting arguments inside the function is a lot safer. What you do here is undefined behavior, and undefined behavior of very practical sort.
AndreyT
this way I convert an int to a void* which is much better than converting a void* to an int. I don't see how anything bad can happen . Please tell me ...
Ben
@Ben. You are not converting anything to anything. You are messing around with the function type not the parameter type. You are delibrately subverting the compilers ability to tell the real type of the function. It still passes a void* to your function no conversion is done it places the bit pattern of a void* on the stack. Your function treats the bit pattern on the stack as if it is an integer.
Martin York
@Ben: Your `thread_func` is called with a `void*`, which it interprets as an `int`. So you still convert (and how could you avoid it?), only it's done with function parameters on the stack. That's probably less safe than the explicit cast. Plus you cheated with function pointer types. Yuck.
sbi
+1  A: 

Don't pass your int as a void*, pass a int* to your int, so you can cast the void* to an int* and copy the dereferenced pointer to your int.

int x = *static_cast<int*>(arg);
stefaanv
If you do this, you have the thread reference a value that (hopefully still) lives in some other thread. That could create all kinds of trouble.
sbi
pthread passes the argument as a void*. This is an old C callback mechanism so you can't change that. And you can't pass a pointer to a stack based object from the other thread as it may no longer be valid.
Martin York
There are ways to prevent this: pass a dynamic allocated argument if your not the passing thread is not static or if your argument is a local variable, otherwise there is no issue.
stefaanv
I agree passing a dynamically allocated pointer is fine (and I think the best way). But you seem to suggest by your answer that the user can pass 5 to pthread_create and then perform the above cast to get it back.
Martin York
+2  A: 

Casting a pointer to void* and back is valid use of reinterpret_cast<>. So you could do this:

pthread_create(&thread, NULL, myFcn, new int(5)); // implicit cast to void* from int*

Then in myFcn:

void* myFcn(void* arg)
{
    int*  data = reinterpret_cast<int*>(arg);
    int   x    = *data;
    delete data;

Note: As sbi points out this would require a change on the OP call to create the thread.

What I am trying to emphasis that conversion from int to pointer and back again can be frough with problems as you move from platform to platform. BUT converting a pointer to void* and back again is well supported (everywhere).

Thus as a result it may be less error prone to generate a pointer dynamcially and use that. Remembering to delete the pointer after use so that we don't leak.

Martin York
sbi
What I am saying is that it would be safer to use new(5) rather than 5 and deal with it appropriately at the other end.
Martin York
I understood, but that would introduce dynamic memory and ugly lifetime issues (an object allocated by one thread must be freed by some other) - all just to pass an `int` to a thread.
sbi
Even if there are issues with freeing the int in a thread (which there usually is not). Then loosing one int to memory management seems preferable to getting the wrong int at the other end.
Martin York
Ugh. I strongly disagree. And casting an `int` to `void*` and back can only be a problem on a platform where `sizeof(int)>sizeof(void*)`. I'm not sure the standard even allows such platforms.
sbi
There is absolutely not gurantee that sizeof(int) <= sizeof(void*). Infact I know several systems where that does not hold.
Martin York
@Martin: Thanks for setting me straight on what the standard does (not) require regarding the size of built-ins. But which are those platforms? And do they implement Posix threads anyway?
sbi
Off the top of my head: KSR-64. Which supported 64 bit address space and 128 bit integers. (PS the 64 in KSR-64 is the number of processors not the address size :-) It ran a standard version of Unix (forget which veriant) that include pthread support. A lot of compilers also support using larger integers with just an extra flag.
Martin York
@Martin: That's one, and it's one even google hasn't heard about. (Note that the C++ code written by me runs on half a dozen of different platforms - and that's only because I consider all Linux versions as a single platform -, so I'm not as Wintel-centric as many others.) Please be reasonable. There might even be platforms where you are not allowed to cast between `int*` and `void*`.
sbi
@sbi: Standard gurantees that casting any pointer to void* and back again will result in a valid value. As for KSR: http://en.wikipedia.org/wiki/Kendall_Square_Research Also even gcc can support 128 bit integers:http://gcc.gnu.org/ml/gcc/2000-09/msg00416.html
Martin York
@SBI: At my last company I was responcable for building ACE/TAO across 32 different platfrom configuration (Hardware/OS/Compiler) and for each configuration we had 8 different build types (Debug-release/Single-Multi Thread/Shared-Static lib) I did not have the luxary of assuming that anything would work unless it was explicitly stated in the standard and even then there are some gotchas.
Martin York
@sbi. Your method will work on most user desktops for the next 3-5 years or so. But will eventually fail. My method is guranteed to work by the standard.
Martin York
@sbi: even if casting int to void* works, imaging now having to pass 2 ints to a thread, or a string? are int's so special? (obviously, they're special enough for some to provide an intptr_t type)
stefaanv
A: 

What you may want is

int x = reinterpret_cast<int>(arg);

This allows you to reinterpret the void * as an int.

Artelius
Note: This is only appropriate is you cast the `int` to a `void *` in the first place.
Artelius
@Artelius: Which, presumably, is exactly what Joshua did: `pthread_create(`.
sbi
A C++ reinterpret cast will not solve the problem. If the original type is a void *, converting to an int may lose date on platforms where sizeof(void *) != sizeof(int) (which is true of LP64 programming model).
R Samuel Klatchko
Hmm? As long as `sizeof(void *) > sizeof(int)`, there is no problem, seeing as you first cast from `int` to `void *` and then back again.
Artelius
A: 

If you call your thread creation function like this

pthread_create(&thread, NULL, myFcn, reinterpret_cast<void*>(5));

then the void* arriving inside of myFcn has the value of the int you put into it. So you know you can cast it back like this

int myData = reinterpret_cast<int>(arg);

even though the compiler doesn't know you only ever pass myFcn to pthread_create in conjunction with an integer.

Edit:

As was pointed out by Martin, this presumes that sizeof(void*)>=sizeof(int). If your code has the chance to ever be ported to some platform where this doesn't hold, this won't work.

sbi
+2  A: 

There's no proper way to cast this to int in general case. C99 standard library provides intptr_t and uintptr_t typedefs, which are supposed to be used whenever the need to perform such a cast comes about. If your standard library (even if it is not C99) happens to priovide these types - use them. If not, check the pointer size on your platform, define these typedefs accordingly yourself and use them.

AndreyT