views:

1383

answers:

11

I have a package that compiles and works fine on a 32-bit machine. I am now trying to get it to compile on a 64-bit machine and find the following error-

 error: cast from ‘void*’ to ‘int’ loses precision

Is there a compiler flag to suppress these errors? or do I have to manually edit these files to avoid these casts?

+4  A: 

You have to manually edit those files in order to replace them with code that isn't likely to be buggy and nonportable.

silentbicycle
Oh well.. I suspected as much.. Thanks..
badkya
+4  A: 

Casting a pointer to an int is horrible from a portability perspective. The size of int is defined by the mix of compiler and architecture. This is why the stdint.h header was created, to allow you to explicitly state the size of the type you're using across many different platforms with many different word sizes.

You'd be better off casting to a uintptr_t or intptr_t (from stdint.h, and choose the one that best matches the signedness you need).

Matthew Iselin
+2  A: 

You can try to use intptr_t for best portability instead of int where pointer casts are required, such as callbacks.

LiraNuna
+18  A: 

The issue is that, in 32bits, an int (which is a 32bit integer) will hold a pointer value.

When you move to 64bit, you can no longer store a pointer in an int - it isn't large enough to hold a 64bit pointer. The intptr_t type is designed for this.

Reed Copsey
+3  A: 

You do not want to suppress these errors because most likely, they are indicating a problem with the code logic.

If you suppresses the errors, this could even work for a while. While the pointer points to an address in the first 4 GB, the upper 32 bits will be 0 and you won't lose any data. But once you get an address > 4GB, your code will start 'mysteriously' not working.

What you should do is modify any int that can hold a pointer to intptr_t.

R Samuel Klatchko
Actually, it's only likely to work well if it's in the first 2G - from 2~4G sign extension will get you :)
bdonlan
+9  A: 

It's an error for a reason: int is only half as big as void* on your machine, so you can't just store a void* in an int. You would loose half of the pointer and when the program later tries to get the pointer out of that int again, it won't get anything useful.

Even if the compiler wouldn't give an error the code most likely wouldn't work. The code needs to be changed and reviewed for 64bit compatibility.

sth
A: 

The type long and void * are guaranteed to be the same size (at least on gnu compilers). You should use unsigned longs when converting back and forth from pointers.

Rob Curtis
-1 sorry, this hack is as ugly as the original assumption that `sizeof (int) == sizeof (void *)`. Different compilers (and different versions of a compiler) are free to use different sizes for these types. Instead use `intptr_t` as Reed Copsey suggests -- this will *always* be the right size.
j_random_hacker
+2  A: 

Suppressing the warnings are a bad idea, but there may be a compiler flag to use 64-bit ints, depending on your compiler and architecture, and this is a safe way to fix the problem (assuming of course that the code didn't also assume ints are 32-bit). For gcc, the flag is -m64.

The best answer is still to fix the code, I suppose, but if it's legacy third-party code and these warnings are rampant, I can't see this refactoring as being a very efficient use of your time. Definitely don't cast pointers to ints in any of your new code, though.

Yes, this is not my code... I ended up casting the pointer to long and the code worked.. sort of...
badkya
+6  A: 

Your code is broken. It won't become any less broken by ignoring the warnings the compiler gives you.

What do you think will happen when you try to store a 64-bit wide pointer into a 32-bit integer? Half your data will get thrown away. I can't imagine many cases where that is the correct thing to do, or where it won't cause errors.

Fix your code. Or stay on the 32-bit platform that the code currently works on.

If your compiler defines intptr_t or uintptr_t, use those, as they are integer types guaranteed to be large enough to store a pointer.

If those types are not available, size_t or ptrdiff_t are also large enough to hold a pointer on most (not all) platforms. Or use long (is typically 64-bit on 64-bit platforms on the GCC compiler) or long long (a C99 types which most, but not all compilers, support in C++), or some other implementation-defined integral type that is at least 64 bits wide on a 64-bit platform.

jalf
+1  A: 

As defined by the current C++ standard, there is no integer type which is guaranteed to hold a pointer. Some platforms will have an intptr_t, but this is not a standard feature of C++. Fundamentally, treating the bits of a pointer as if they were an integer is not a portable thing to do (although it can be made to work on many platforms).

If the reason for the cast is to make the pointer opaque, then void* already achieves this, so the code could use void* instead of int. A typedef might make this a little nicer in the code

typedef void * handle_t;

If the reason for the cast is to do pointer arithmetic with byte granularity, then the best way is probably to cast to a (char const *) and do the math with that.

If the reason for the cast is to achieve compatibility with some existing library (perhaps an older callback interface) which cannot be modified, then I think you need to review the documentation for that library. If the library is capable of supporting the functionality that you require (even on a 64-bit platform), then its documentation may address the intended solution.

nobar
A: 

My guess is OP's situation is a void* is being used as general storage for an int, where the void* is larger than the int. So eg:

int i = 123;
void *v = (void*)i;    // 64bit void* being (ab)used to store 32bit value
[..]
int i2 = (int)v;       // we want our 32bits of the 64bit void* back

Compiler doesn't like that last line.

I'm not going to weigh in on whether it's right or wrong to abuse a void* this way. If you really want to fool the compiler, the following technique seems to work, even with -Wall:

int i2 = *((int*)&v);

Here it takes the address of v, converts the address to a pointer of the datatype you want, then follows the pointer.

erco