views:

6873

answers:

6

I am trying to adapt an existing code to a 64 bit machine. The main problem is that in one function, the previous coder uses a void* argument that is converted into suitable type in the function itself. A short example:

void function(MESSAGE_ID id, void* param)
{
    if(id == FOO) {
        int real_param = (int)param;
        // ...
    }
}

Of course, on a 64 bit machine, I get the error:

error: cast from 'void*' to 'int' loses precision

I would like to correct this so that it still works on a 32 bit machine and as cleanly as possible. Any idea ?

+6  A: 

Use intptr_t and uintptr_t

To ensure it is defined in a portable way, you can use code like this:

#if defined(__BORLANDC__)
    typedef unsigned char uint8_t;
    typedef __int64 int64_t;
    typedef unsigned long uintptr_t;
#elif defined(_MSC_VER)
    typedef unsigned char uint8_t;
    typedef __int64 int64_t;
#else
    #include <stdint.h>
#endif

Just place that in some .h file and include wherever you need it.

Alternatively, you can download MS version stdint.h file from here:

http://msinttypes.googlecode.com/svn/trunk/stdint.h

or use a portable one from here:

http://www.azillionmonkeys.com/qed/pstdint.h

Milan Babuškov
so using borland C you cannot compile on a 64 bits machine ?
PierreBdR
See http://stackoverflow.com/questions/126279/c99-stdinth-header-and-ms-visual-studio for info on how to get a stdint.h that works with MSVC (and possibly Borland).
Michael Burr
+2  A: 

Use uintptr_t as your integer type.

moonshadow
A: 

The best thing to do is to avoid converting from pointer type to non-pointer types. However, this is clearly not possible in your case.

As everyone said, the uintptr_t is what you should use.

This link has good info about converting to 64-bit code.

There is also a good discussion of this on comp.std.c

Benoit
A: 

Why not pass a pointer to that integer?

aib
Probably because there are untold references to that call possibly in code that, for whatever reason, can't be changed. Sometimes you just have to hold your nose and do what you can to keep the old crap working.
Darcy Casselman
And when you remember it's YOUR crap, you shoot yourself in the foot with it - if you can.
Sometimes (albeit rarely) an extra pointer dereference matters.
Constantin
+2  A: 

Several answers have pointed at uintptr_t and #include <stdint.h> as 'the' solution. That is, I suggest, part of the answer, but not the whole answer. You also need to look at where the function is called with the message ID of FOO.

Consider this code and compilation ('Black JL:' is my command prompt on machine black):

Black JL: cat kk.c
#include <stdio.h>
static void function(int n, void *p)
{
    unsigned long z = *(unsigned long *)p;
    printf("%d - %lu\n", n, z);
}

int main(void)
{
    function(1, 2);
    return(0);
}
Black JL: rmk kk
        gcc -m64 -g -O -std=c99 -pedantic -Wall -Wshadow \
        -Wpointer-arith -Wcast-qual  -Wstrict-prototypes -Wmissing-prototypes \
        -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE kk.c -o kk 
kk.c: In function 'main':
kk.c:10: warning: passing argument 2 of 'func' makes pointer from integer without a cast
Black JL:

You will observer that there is a problem at the calling location (in main()) - converting an integer to a pointer without a cast. You are going to need to analyze your function() in all its usages to see how values are passed to it. The code inside my function() would work if the calls were written:

unsigned long i = 0x2341;
function(1, &i);

Since yours are probably written differently, you need to review the points where the function is called to ensure that it makes sense to use the value as shown. Don't forget, you may be finding a latent bug.

Also, if you are going to format the value of the void * parameter (as converted), look carefully at the <inttypes.h> header (instead of stdint.h - inttypes.h provides the services of stdint.h, which is unusual, but the C99 standard says [t]he header <inttypes.h> includes the header <stdint.h> and extends it with additional facilities provided by hosted implementations) and use the PRIxxx macros in your format strings.

Also, my comments are strictly applicable to C rather than C++, but your code is in the subset of C++ that is portable between C and C++. The chances are fair to good that my comments apply.

Jonathan Leffler
+5  A: 

'size_t' and 'ptrdiff_t' are required to match your architecture (whatever it is). Therefore, I think rather than using 'int', you should be able to use 'size_t', which on a 64 bit system should be a 64 bit type.

This discussion unsigned int vs size_t goes into a bit more detail.

Richard Corden
While size_t is usually large enough to hold a pointer, it's not necessarily the case. It would be better to locate a stdint.h header (if your compiler doesn't already have one) and use uintptr_t.
Michael Burr