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 ?
I'd say this is the modern C++ way:
#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);
EDIT:
The correct type to the the Integer
So the right way to store a pointer as an integer is to use the uintptr_t or intptr_t types. (See also in cppreference integer types for C99).
These types are defined in <stdint.h> for C99 and in the namespace std for C++11 in <cstdint> (see integer types for C++).
C++11 (and onwards) Version
#include <cstdint>
std::uintptr_t i;
C++03 Version
extern "C" {
#include <stdint.h>
}
uintptr_t i;
C99 Version
#include <stdint.h>
uintptr_t i;
The correct casting operator
In C there is only one cast and using the C cast in C++ is frowned upon (so don't use it in C++). In C++ there are different types of casts, but reinterpret_cast is the correct cast for this conversion (see also here).
C++11 Version
auto i = reinterpret_cast<std::uintptr_t>(p);
C++03 Version
uintptr_t i = reinterpret_cast<uintptr_t>(p);
C Version
uintptr_t i = (uintptr_t)p; // C Version
Related Questions
What is uintptr_t data type
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 Microsoft’s version of the stdint.h file from here or use a portable one from here.
'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.
Use uintptr_t as your integer type.
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:
$ 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);
}
$ 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
$
You will observe 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.
#include <stdint.h>
Use uintptr_t standard type defined in the included standard header file.
I came across this question while studying the source code of SQLite.
In the sqliteInt.h, there is a paragraph of code defined a macro convert between integer and pointer. The author made a very good statement first pointing out it should be a compiler dependent problem and then implemented the solution to account for most of the popular compilers out there.
#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
#else /* Generates a warning - but it always works */
# define SQLITE_INT_TO_PTR(X) ((void*)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(X))
#endif
And here is a quote of the comment for more details:
/*
** The following macros are used to cast pointers to integers and
** integers to pointers. The way you do this varies from one compiler
** to the next, so we have developed the following set of #if statements
** to generate appropriate macros for a wide range of compilers.
**
** The correct "ANSI" way to do this is to use the intptr_t type.
** Unfortunately, that typedef is not available on all compilers, or
** if it is available, it requires an #include of specific headers
** that vary from one machine to the next.
**
** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on
** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)).
** So we have to define the macros in different ways depending on the
** compiler.
*/
Credit goes to the committers.
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
I think the "meaning" of void* in this case is a generic handle.
It is not a pointer to a value, it is the value itself.
(This just happens to be how void* is used by C and C++ programmers.)
If it is holding an integer value, it had better be within integer range!
Here is easy rendering to integer:
int x = (char*)p - (char*)0;
It should only give a warning.
Since uintptr_t is not guaranteed to be there in C++/C++11, if this is a one way conversion you can consider uintmax_t, always defined in <cstdint>.
auto real_param = reinterpret_cast<uintmax_t>(param);
To play safe, one could add anywhere in the code an assertion:
static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
"No suitable integer type for conversion from pointer type");
With C++11, For what it's worth, suppose you don't have any headers, then define:
template<bool B, class T, class F> struct cond { typedef T type; };
template<class T, class F> struct cond<false, T, F> { typedef F type;};
static constexpr unsigned int PS = sizeof (void *);
using uintptr_type = typename cond<
PS==sizeof(unsigned short), unsigned short ,
typename cond<
PS==sizeof(unsigned int), unsigned int,
typename cond<
PS==sizeof(unsigned long), unsigned long, unsigned long long>::type>::type>::type;
After that you can do the following:
static uintptr_type ptr_to_int(const void *pointer) {
return reinterpret_cast<uintptr_type>(pointer);
}
static void *int_to_ptr(uintptr_type integer) {
return reinterpret_cast<void *>(integer);
}
Related
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 ?
I'd say this is the modern C++ way:
#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);
EDIT:
The correct type to the the Integer
So the right way to store a pointer as an integer is to use the uintptr_t or intptr_t types. (See also in cppreference integer types for C99).
These types are defined in <stdint.h> for C99 and in the namespace std for C++11 in <cstdint> (see integer types for C++).
C++11 (and onwards) Version
#include <cstdint>
std::uintptr_t i;
C++03 Version
extern "C" {
#include <stdint.h>
}
uintptr_t i;
C99 Version
#include <stdint.h>
uintptr_t i;
The correct casting operator
In C there is only one cast and using the C cast in C++ is frowned upon (so don't use it in C++). In C++ there are different types of casts, but reinterpret_cast is the correct cast for this conversion (see also here).
C++11 Version
auto i = reinterpret_cast<std::uintptr_t>(p);
C++03 Version
uintptr_t i = reinterpret_cast<uintptr_t>(p);
C Version
uintptr_t i = (uintptr_t)p; // C Version
Related Questions
What is uintptr_t data type
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 Microsoft’s version of the stdint.h file from here or use a portable one from here.
'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.
Use uintptr_t as your integer type.
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:
$ 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);
}
$ 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
$
You will observe 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.
#include <stdint.h>
Use uintptr_t standard type defined in the included standard header file.
I came across this question while studying the source code of SQLite.
In the sqliteInt.h, there is a paragraph of code defined a macro convert between integer and pointer. The author made a very good statement first pointing out it should be a compiler dependent problem and then implemented the solution to account for most of the popular compilers out there.
#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
#else /* Generates a warning - but it always works */
# define SQLITE_INT_TO_PTR(X) ((void*)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(X))
#endif
And here is a quote of the comment for more details:
/*
** The following macros are used to cast pointers to integers and
** integers to pointers. The way you do this varies from one compiler
** to the next, so we have developed the following set of #if statements
** to generate appropriate macros for a wide range of compilers.
**
** The correct "ANSI" way to do this is to use the intptr_t type.
** Unfortunately, that typedef is not available on all compilers, or
** if it is available, it requires an #include of specific headers
** that vary from one machine to the next.
**
** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on
** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)).
** So we have to define the macros in different ways depending on the
** compiler.
*/
Credit goes to the committers.
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
I think the "meaning" of void* in this case is a generic handle.
It is not a pointer to a value, it is the value itself.
(This just happens to be how void* is used by C and C++ programmers.)
If it is holding an integer value, it had better be within integer range!
Here is easy rendering to integer:
int x = (char*)p - (char*)0;
It should only give a warning.
Since uintptr_t is not guaranteed to be there in C++/C++11, if this is a one way conversion you can consider uintmax_t, always defined in <cstdint>.
auto real_param = reinterpret_cast<uintmax_t>(param);
To play safe, one could add anywhere in the code an assertion:
static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
"No suitable integer type for conversion from pointer type");
With C++11, For what it's worth, suppose you don't have any headers, then define:
template<bool B, class T, class F> struct cond { typedef T type; };
template<class T, class F> struct cond<false, T, F> { typedef F type;};
static constexpr unsigned int PS = sizeof (void *);
using uintptr_type = typename cond<
PS==sizeof(unsigned short), unsigned short ,
typename cond<
PS==sizeof(unsigned int), unsigned int,
typename cond<
PS==sizeof(unsigned long), unsigned long, unsigned long long>::type>::type>::type;
After that you can do the following:
static uintptr_type ptr_to_int(const void *pointer) {
return reinterpret_cast<uintptr_type>(pointer);
}
static void *int_to_ptr(uintptr_type integer) {
return reinterpret_cast<void *>(integer);
}
The type std::size_t is an unsigned type that can store the maximum size of a theoretically possible object of any type, and std::ptrdiff_t is a signed type that can hold the difference of two pointers (which is another way of saying that it should be the standard integer for array indexing in C++). The C++ standard library has decided to use std::size_t for array indexing but it is often stated that std::ptrdiff_t is a better choice. Which one is the best is a long debate which I don't want to enter here, but I have always thought that the second is the unsigned version of the first. Running this program on macOS give
#include <cstddef>
#include <cstdio>
void f(int n) { std::printf("int"); };
void f(long n) { std::printf("long"); };
void f(long long n) { std::printf("long long"); };
void f(unsigned int n) { std::printf("unsigned int"); };
void f(unsigned long n) { std::printf("unsigned long"); };
void f(unsigned long long n) { std::printf("unsigned long long"); };
int main() {
const std::ptrdiff_t n_ptrdiff = 0;
const std::size_t n_size = 0;
std::printf("std::ptrdiff_t is an alias for ");
f(n_ptrdiff);
std::printf("\n");
std::printf("std::size_t is an alias for ");
f(n_size);
std::printf("\n");
return 0;
}
On a 64-bit platform but compiled for 32-bit:
clang++ -m32 -std=c++11 type.cpp -o type-32-clang
gives me the following result:
std::ptrdiff_t is an alias for int
std::size_t is an alias for unsigned long
In this case std::size_t is not the unsigned version of std::ptrdiff_t even though they have the same storage size (4 bytes). Although it is in conformance with the standard, it seems very weird to me.
Does anyone knows of a rationale for this choice?
TL;DR: For compatibility with other systems, which did this for compatibility with yet other systems, which etc.
clang would normally have had signed long and unsigned long for ptrdiff_t and size_t as you expected, but commit "fix some differences between apple gcc and clang on darwin/x86-32" changed ptrdiff_t to signed int for compatibility with gcc. You can't have clang and gcc use the same C++ libraries if they disagree on basic typedefs such as these.
gcc uses unsigned long for size_t but signed int for ptrdiff_t because that's what Apple contributed: commit "Add Darwin (Mac OS X kernel) native support." shows in gcc/config/darwin.h:
/* Target definitions for Darwin (Mac OS X) systems.
Copyright (C) 1989, 1990, 1991, 1992, 1993, 2000, 2001
Free Software Foundation, Inc.
Contributed by Apple Computer Inc.
[...]
/* The string value for __SIZE_TYPE__. */
#ifndef SIZE_TYPE
#define SIZE_TYPE "long unsigned int"
#endif
/* Type used for ptrdiff_t, as a string used in a declaration. */
#undef PTRDIFF_TYPE
#define PTRDIFF_TYPE "int"
No specific reason is mentioned, but this file is not specific to any processor type, it applies to all Darwin systems, and the commit also touches gcc/config/rs6000/rs6000.h, making it likely that it was for compatibility with that, bringing it back to 1995 for commit "Add preliminary V.4 and eABI support.":
/* Type used for ptrdiff_t, as a string used in a declaration. */
#undef PTRDIFF_TYPE
#define PTRDIFF_TYPE "int"
Since this doesn't redefine SIZE_TYPE, that's left at the default value of "unsigned long".
gcc was not the system compiler for this platform, so that in turn was likely for compatibility with IBM's compiler. Which seems to be supported by gcc/config/rs6000/aix43.h:
/* AIX 4.3 typedefs ptrdiff_t as "long" while earlier releases used "int". */
#undef PTRDIFF_TYPE
#define PTRDIFF_TYPE "long int"
Going back further than that probably isn't going to happen, since the history for AIX is not public.
I need a way to get the largest type available on the target compiler architecture. I'd expect a define along the lines of:
#if defined PLATFORM_32BIT
#define LARGEST_UNSIGNED_TYPE uint32_t
#elseif defined PLATFORM_64BIT
#define LARGEST_UNSIGNED_TYPE uint64_t
#endif
Is there a standard C++ way to achieve this? Or do I need to roll my own?
If you can use uint64_t you should also be able to use uintmax_t.
Header <cstdint>in C++ (and <stdint.h> in C) already contains the following definitions
typedef signed integer type intmax_t;
and
typedef unsigned integer type uintmax_t;
I would like to profit by int64_t for my int, when present in a c++98 or greater implementation (I know C++11 already provides a 64-bit int).
Is there a portable and generic way to check the presence of int64_t or - even better - any 64-bit-or-greater integer type available, as in:
#ifdef has_int64_t
typedef int64_t myint
#else
typedef int myint
#endif
//...code
myint a;
If possible the check should work for c++98, c++03 and c++11.
Pretty sure this should work using stdint.h:
#ifdef INT64_C
//...
#else
//...
#endif
The situation with stdint.h and the C++11 cstdint headers is rather tricky. Especially since they are missing on certain VS versions. The easiest route is certainly boost stdint.
In C, it's not an error to cast pointers to and from void *.
A major obstacle in porting to C++ is the need to cast pointers when returning from functions dealing with generic pointers such as malloc, and functions declared in my own code such as void *block_get(Blkno const blkno);.
My code however is intended to be compiled by C and C++ compilers successfully. If I provide explicit casts everywhere for the sake of C++, they must be C-style casts and I may be masking bugs due to casting non-pointer types to and from pointer types from both languages.
My reference error is the following:
struct Cpfs *cpfs = calloc(1, sizeof(*cpfs));
which in MSVC produces:
Error 2 error C2440: 'initializing' : cannot convert from 'void *' to 'Cpfs *' e:\src\cpfs\cpfs.c 179
Evidently I can't use new or static_cast which I'd naturally use if I was no longer using C. What's the best way to provide maximum type safety surrounding void *for each language with minimal verbosity?
I'd suggest either simply using C style casts, or wrapping the cast in a macro that either expands to nothing (in C), or a static_cast in C++.
If your compiler supports decltype(), you can use some macro magic to avoid having to explicitly repeat the type name (and, thanks to sizeof, the element size):
#ifdef __cplusplus
#define my_calloc(VAR, COUNT) \
static_cast<decltype(VAR)>(std::calloc(COUNT, sizeof *VAR))
#else
#define my_calloc(VAR, COUNT) calloc(COUNT, sizeof *VAR)
#endif
Example usage:
#ifdef __cplusplus
#include <cstdlib>
#else
#include <stdlib.h>
#endif
struct Cpfs *cpfs = my_calloc(cpfs, 42);
The cleaner solution would probably be to just use a C compiler and link the object files, though...
make a replacement allocator function that you can define differently for C and C++ builds :- Something like this in a header file:
#ifdef __cplusplus
template<typename TypeT>
TypeT* MyAlloc(TypeT** pOut,size_t cb){
*pOut = static_cast<TypeT*>(malloc(cb)); //aint c++ pretty.
return *pOut;
}
#else
extern void* MyAlloc(void** ppv, size_t cb);
#endif
Now you have, in c++ builds, a function that can infer the type of thing its dealing with, and in C builds, its a regular function that returns a void*.
The only problem is the need to pass in the pointer to allocate - the c++ compiler wont try to deduce a template parameter based only on the return type of a function afaik. So you could call it agnostically like this :-
int *p;
if(MyAlloc(&p,sizeof(int)*n)){
...
Maybe something like this? (untested, no compiler available, not using macros very often):
#ifdef __cplusplus
#define pointer_cast(type, pointer) reinterpret_cast<type>(pointer)
#else
#define pointer_cast(type, pointer) (type)(pointer)
#endif
The only solution I know is to do explicit casting:
struct Cpfs *cpfs = (Cpfs*)calloc(1, sizeof(*cpfs));
Here both compilers are satisfied.
Also that remember, that for older compilers malloc may return char*.
hth
Mario