Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How to portably cast from void* to int

46 views
Skip to first unread message

Melzzzzz

unread,
Jun 16, 2015, 10:52:13 AM6/16/15
to
Problem is this:

void f(void*p){ // c callback
int i = (int)p;
...
}

but this gives error when p is 64 bit and int 32 bit.
I can cast to long but long does not have to be 64 bit,either.
What do you suggest?
Message has been deleted

Victor Bazarov

unread,
Jun 16, 2015, 11:03:54 AM6/16/15
to
I suggest the type that can contain the value. The standard does not
provide a type that fits, leaving it to the implementation. If 'long'
is not large enough, use 'long long'.

Usually you need to write some implementation-specific conditionally
compiled code with typedefs producing the needed types, like

#if WIN32
typedef long integraltypeforstoringpointers;
#elif WIN64
typedef long long integraltypeforstoringpointers;
#elif ...
...


So, then you use that type to cast. But please don't use C-style casts,
use 'reinterpret_cast', that's exactly what it's for. E.g.:

integraltypeforstoringpointers i
= reinterpret_cast<integraltypeforstoringpointers>(p);

and the length of the statement and type name should be a warning that
you are doing something a bit unusual, and anybody who reads the code
needs to pay attention.

V
--
I do not respond to top-posted replies, please don't ask

Robert Wessel

unread,
Jun 16, 2015, 12:15:12 PM6/16/15
to
intptr_t and/or uintptr_t from cstdint. Although they don't
have to exist.

Scott Lurndal

unread,
Jun 16, 2015, 12:28:43 PM6/16/15
to
use the type 'uintptr_t' (C++11 and later)

Richard

unread,
Jun 16, 2015, 1:47:17 PM6/16/15
to
[Please do not mail me a copy of your followup]

Robert Wessel <robert...@yahoo.com> spake the secret code
<iri0oat3n1mjik4e9...@4ax.com> thusly:

>intptr_t and/or uintptr_t from cstdint. Although they don't
>have to exist.

It's kinda annoying that these are optional.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>

Melzzzzz

unread,
Jun 16, 2015, 2:11:16 PM6/16/15
to
On Tue, 16 Jun 2015 11:03:44 -0400
Victor Bazarov <v.ba...@comcast.invalid> wrote:

> On 6/16/2015 10:51 AM, Melzzzzz wrote:
> > Problem is this:
> >
> > void f(void*p){ // c callback
> > int i = (int)p;
> > ...
> > }
> >
> > but this gives error when p is 64 bit and int 32 bit.
> > I can cast to long but long does not have to be 64 bit,either.
> > What do you suggest?
>
> I suggest the type that can contain the value. The standard does not
> provide a type that fits, leaving it to the implementation. If
> 'long' is not large enough, use 'long long'.

Well, this is actually int passed as void*. Because of C interface
generic parameter is void*. So I have to convert int -> void* -> int

Melzzzzz

unread,
Jun 16, 2015, 2:16:20 PM6/16/15
to
Too ,bad ;(
I need intptr_t as conversion is actually passing int parameter as void*
and than back to int.

Victor Bazarov

unread,
Jun 16, 2015, 2:21:40 PM6/16/15
to
Read about 'reinterpret_cast' in your compiler documentation and see
what limitations it sets. Please don't use a C-style cast, it's ugly,
dangerous, difficult to search for if you want to know where in your
code you're using it.

Richard

unread,
Jun 16, 2015, 3:02:09 PM6/16/15
to
[Please do not mail me a copy of your followup]

Melzzzzz <m...@zzzzz.com> spake the secret code
<20150616201106.5d5b2eda@maxa-pc> thusly:
An alternative is to pass a pointer to the int instead of the int.

Generally these C apis with void* context parameters are intending
that you allocate a chunk of memory and pass the pointer around to
the callbacks instead of raw values. This is more portable than
trying to figure out how to convert between int and void*. Any
pointer is implicitly convertible to a void* and you can static_cast
any void* to a pointer of type T (although the result is undefined
behavior of the void* didn't come from a pointer to type T).

Victor Bazarov

unread,
Jun 16, 2015, 3:15:08 PM6/16/15
to
Allocation of a plain int is often expensive in both memory and time,
and besides, who owns that pointer and who's going to deallocate that
memory when it's not needed anymore? What if a C API call happens
periodically with the same value, do you propose to allocate it every
time and deallocate after the call has returned? What if API is such
that it needs to retain the value for a few calls? A literal would be
much less trouble than managing the lifetime of a single 'int'.

Not saying that such a solution is unacceptable, just that it's not
necessarily easy or as clean as it might appear at a first glance.

Martin Shobe

unread,
Jun 16, 2015, 3:50:35 PM6/16/15
to
Depends on your design, but the typical case is the caller.

> What if a C API call happens
> periodically with the same value, do you propose to allocate it every
> time and deallocate after the call has returned?

Depends on your design, but the typical case is no.

> What if API is such
> that it needs to retain the value for a few calls?

Then it does so.

> A literal would be
> much less trouble than managing the lifetime of a single 'int'.

much less? I think I detect a bit of hyperbole.

> Not saying that such a solution is unacceptable, just that it's not
> necessarily easy or as clean as it might appear at a first glance.

It's not as hard as you are making it out to be. Presumably, the
original poster is doing something like this.

void bar()
{
int x = 42;

....
// foo will call f with x.
foo(f, (void *) x);
...
}

void f(void * p)
{
int i = (int) p;
...
}

The suggested change is to do this instead

void bar()
{
int x = 42;

...
// foo will call f with x.
foo(f, &x);
...
}

void f(void * p)
{
int i = *((int *) p);
...
}

Martin Shobe

Victor Bazarov

unread,
Jun 16, 2015, 3:55:33 PM6/16/15
to
OK. Now consider this. It does so, and it gets called from other
functions of the same API some time later, and the local variable has
gone out of scope and its address contains garbage...

It's an unlikely scenario, yet not completely unheard of, that the API
has a state that it wants to maintain.

>
>> A literal would be
>> much less trouble than managing the lifetime of a single 'int'.
>
> much less? I think I detect a bit of hyperbole.

Not if the lifetime has to be managed across modules or beyond one API call.

>> Not saying that such a solution is unacceptable, just that it's not
>> necessarily easy or as clean as it might appear at a first glance.
>
> It's not as hard as you are making it out to be. Presumably, the
> original poster is doing something like this.
>
> void bar()
> {
> int x = 42;
>
> ....
> // foo will call f with x.
> foo(f, (void *) x);
> ...
> }
>
> void f(void * p)
> {
> int i = (int) p;
> ...
> }
>
> The suggested change is to do this instead
>
> void bar()
> {
> int x = 42;
>
> ...
> // foo will call f with x.
> foo(f, &x);
> ...
> }
>
> void f(void * p)
> {
> int i = *((int *) p);
> ...
> }

Bad idea using C-style casts, that one of the points I failed to
deliver, I am guessing...

David Brown

unread,
Jun 16, 2015, 4:20:32 PM6/16/15
to
That's fine as long as x is still alive before f gets called. But often
these sorts of things are for registering some sort of callback - foo
won't actually call f directly, but arrange for it to be called later.
Thus you have to make sure x is still alive after bar has exited - and
that means dynamic memory and a lot of pain.

A far better arrangement is to use a union instead of a void*, so that
you can pass either small value data (such as an int or two) or a
pointer to larger data.

Chris Vine

unread,
Jun 16, 2015, 4:37:19 PM6/16/15
to
This is a common idiom in C libraries, and if you are doing the
conversions that way, and all your numerical manipulation is done using
ints, you don't need to worry about whether the size of void* is larger
than the size of int. If you begin with a value in the range of int you
will end with a value within the range of int and the conversion will
work. (You do have to worry if the size of int is larger than the size
of void*, but I know of no implementation where that is the case.)

Chris


Richard

unread,
Jun 16, 2015, 5:07:41 PM6/16/15
to
[Please do not mail me a copy of your followup]

Victor Bazarov <v.ba...@comcast.invalid> spake the secret code
<mlpsh2$4c0$1...@dont-email.me> thusly:
Code for simplicity, readability and understanding first and get all
obsessive about allocating a single int later only if it actually
matters. (I'm really hard-pressed to think of a single case where
allocating a single int is really going to matter.)

>and besides, who owns that pointer and who's going to deallocate that
>memory when it's not needed anymore?

All these C-style APIs that use void* context pointers have ways of
managing the lifetime of the memory. Remember, they already have to
solve that problem for C; it is no different for C++.

>What if a C API call happens
>periodically with the same value, do you propose to allocate it every
>time and deallocate after the call has returned?

The original poster didn't specify this, but in general, this is NOT
how C style callback APIs work when they use a void* context pointer.
I've used plenty of them (the X11 Toolkit, Xt, is riddled with them).

>Not saying that such a solution is unacceptable, just that it's not
>necessarily easy or as clean as it might appear at a first glance.

While these concerns aren't unreasonable for some unspecified API, it
is not typically how C style APIs with callbacks work. If they are
doing any of the things you raise, then their API is simply broken both
for C and C++.

Melzzzzz

unread,
Jun 16, 2015, 5:07:51 PM6/16/15
to
That is the case.

>
> A far better arrangement is to use a union instead of a void*, so
> that you can pass either small value data (such as an int or two) or
> a pointer to larger data.
>

Can't change framework.

Victor Bazarov

unread,
Jun 16, 2015, 5:17:11 PM6/16/15
to
On 6/16/2015 5:07 PM, Richard wrote:
> [..]
> The original poster didn't specify this, but in general, this is NOT
> how C style callback APIs work when they use a void* context pointer.

"All generalizations are wrong". Think about it.

> I've used plenty of them (the X11 Toolkit, Xt, is riddled with them).
> [..]

Chris Vine

unread,
Jun 16, 2015, 7:43:05 PM6/16/15
to
On Tue, 16 Jun 2015 21:37:00 +0100
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
[snip]
> > Too ,bad ;(
> > I need intptr_t as conversion is actually passing int parameter as
> > void* and than back to int.
>
> This is a common idiom in C libraries, and if you are doing the
> conversions that way, and all your numerical manipulation is done
> using ints, you don't need to worry about whether the size of void*
> is larger than the size of int. If you begin with a value in the
> range of int you will end with a value within the range of int and
> the conversion will work. (You do have to worry if the size of int
> is larger than the size of void*, but I know of no implementation
> where that is the case.)

On looking it up I think on the last point I was too pessimistic. It
seems to me that reinterpret casts of int->void*->int are obliged to
work according to the standard - in other words, sizeof(int) cannot be
larger than sizeof(void*) in a conforming implementation. In fact, it
appears that sizeof(long long) cannot be larger than sizeof(void*):

§5.2.10/4 of C++11: "A pointer can be explicitly converted to any
integral type large enough to hold it. The mapping function is
implementation-defined. [ Note: It is intended to be unsurprising to
those who know the addressing structure of the underlying machine. —
end note ]. ... ."

§5.2.10/5 of C++11: "A value of integral type or enumeration type can
be explicitly converted to a pointer. A pointer converted to an
integer of sufficient size (if any such exists on the implementation)
and back to the same pointer type will have its original value;
mappings between pointers and integers are otherwise
implementation-defined. ... ."

In the first sentence of §5.2.10/5 there is no qualification about sizes
for the integer->pointer conversion. The reverse conversion does have a
size qualification.

Chris






Chris Vine

unread,
Jun 16, 2015, 8:13:06 PM6/16/15
to
On Wed, 17 Jun 2015 00:42:48 +0100
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> On Tue, 16 Jun 2015 21:37:00 +0100
> Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
> [snip]
> > > Too ,bad ;(
> > > I need intptr_t as conversion is actually passing int parameter as
> > > void* and than back to int.
> >
> > This is a common idiom in C libraries, and if you are doing the
> > conversions that way, and all your numerical manipulation is done
> > using ints, you don't need to worry about whether the size of void*
> > is larger than the size of int. If you begin with a value in the
> > range of int you will end with a value within the range of int and
> > the conversion will work. (You do have to worry if the size of int
> > is larger than the size of void*, but I know of no implementation
> > where that is the case.)
>
> On looking it up I think on the last point I was too pessimistic. It
> seems to me that reinterpret casts of int->void*->int are obliged to
> work according to the standard - in other words, sizeof(int) cannot be
> larger than sizeof(void*) in a conforming implementation. In fact, it
> appears that sizeof(long long) cannot be larger than sizeof(void*):

[snip]

Well, whatever the theory, it is not borne out by practice. On the
32-bit machines on which I have just now tested, sizeof(long long)
certainly is larger than sizeof (void*). The first is 8 and the
second is 4. But it is curious that the qualification about sizes is
omitted in the standard for integral to pointer conversions.

Chris

Martin Shobe

unread,
Jun 16, 2015, 10:02:12 PM6/16/15
to
Obviously, you have to keep it around for as long as the API needs it.

> It's an unlikely scenario, yet not completely unheard of, that the API
> has a state that it wants to maintain.

>>> A literal would be
>>> much less trouble than managing the lifetime of a single 'int'.
>>
>> much less? I think I detect a bit of hyperbole.
>
> Not if the lifetime has to be managed across modules or beyond one API
> call.

Even then. The API, in order to be usable, should have documentation
that tells you how long you have to keep it around. While I won't claim
that every case is easy, I've never encountered one where it would be hard.
I won't disagree about the use of C-style casts, it just wasn't
important for my point so I didn't change it.

Martin Shobe

Bo Persson

unread,
Jun 17, 2015, 11:20:12 AM6/17/15
to
On 2015-06-16 19:47, Richard wrote:
> [Please do not mail me a copy of your followup]
>
> Robert Wessel <robert...@yahoo.com> spake the secret code
> <iri0oat3n1mjik4e9...@4ax.com> thusly:
>
>> intptr_t and/or uintptr_t from cstdint. Although they don't
>> have to exist.
>
> It's kinda annoying that these are optional.
>

Yes, but it allows C and C++ to be implemented on systems where void* is
larger than all integer types.

For example word-addresses machines where char* and void* can be larger
than int*.



Bo Persson

Bo Persson

unread,
Jun 17, 2015, 11:24:42 AM6/17/15
to
If you limit yourself to systems where the types exist, that might be
all you need. Systems where these types are missing are extremely rare,
and you might not want to run your programs there anyway, because of
other peculiarities of these systems (like 48-bit ints, etc).


Bo Persson

Richard

unread,
Jun 18, 2015, 12:13:46 AM6/18/15
to
[Please do not mail me a copy of your followup]

Victor Bazarov <v.ba...@comcast.invalid> spake the secret code
<mlq3m0$ec$2...@dont-email.me> thusly:

>On 6/16/2015 5:07 PM, Richard wrote:
>> [..]
>> The original poster didn't specify this, but in general, this is NOT
>> how C style callback APIs work when they use a void* context pointer.
>
>"All generalizations are wrong". Think about it.

But it is simply pointless to raise these "what if?" questions that
arise from the realm of a "hostile API" when the normal case is not a
hostile API. If you have a hostile API, then you've got bigger fish
to fry.

There is an infinite number of hostile APIs we can imagine where doing
something sane is difficult or impossible, but it is a waste of time
discussion them.
0 new messages