C++ 64 bit void */int conversion addressing questions

Started by
7 comments, last by frob 9 years, 4 months ago

Hello,

I am currently upgrading a game engine from a 32bit system to a 64 bit system. there are definate spots where the original author was holding a pointer in an int.

struct myrefdata

{

int data;

};

now i want to upgrade this to a 64 bit system. my initial idea was that this should be a void * instead of an int so

struct myrefdata

{

void *data;

};

now what i am finding is where the author did intend to actually hold an int in this field needs to be adjusted.

int x = 100;

myRefData->data = x;

int y = (int)myRefData->data;

while this example is a large oversimplication. i was wondering for those with more 64bit experience that me for assistance.

i am using c++11 on all my target platforms.

how can i correctly convert a int to a void * and a void * to int, for the times i do want to hold an int in a void *?

another question...

assuming that there are other sections of the game engine where it is just impossible to convert that int to a void * but it does hold a pointer, is there a way i can force a memalloc in the 32bit space? or if i cant do that declare an array in global space of the program to hold the pointer and put this array in 32bit memory space.

very much appreciate any help anyone could provide.

Advertisement

Are you looking for an union?

no not looking to use a union

basically i'm looking to know how i have to modifiy this code inorder for it to work as intended.

int x = 100;

myRefData->data = x;

int y = (int)myRefData->data;.

no not looking to use a union

basically i'm looking to know how i have to modifiy this code inorder for it to work as intended.

int x = 100;
myRefData->data = x;
[background=#fafbfc]int y = (int)myRefData->data;[/background]

.

A union is the "proper" way to "fix" this assuming you cannot rewrite the code to not store pointers and ints in the same place to begin with. Whatever you end up doing subject to that limitation will have to break aliasing rules anyway, so you might as well do it the type-safe way and by the same token ensure your pointer doesn't get mysteriously truncated on 64-bit systems.

“If I understand the standard right it is legal and safe to do this but the resulting value could be anything.”

hmm. that is really unfortunate. i really didn't want to redefine all methods that take ints to now take a union instead. i guess though it is better to bite the bullet...

hmm. that is really unfortunate. i really didn't want to redefine all methods that take ints to now take a union instead. i guess though it is better to bite the bullet...

The union is only for inside your myrefdata struct.
If those methods are actually passing around integer data (not pointers), then they can stay as ints.

Also, instead of int, the original programmer should have used intptr_t -- this is an int that's big enough to hold any pointer.
Or perhaps ptrdiff_t -- this is an int that's big enough to hold the difference between any two pointers.

assuming that there are other sections of the game engine where it is just impossible to convert that int to a void * but it does hold a pointer, is there a way i can force a memalloc in the 32bit space?

Yeah...
On windows, you want to completely replace all instances of new/malloc/etc with your own memory allocator, which uses ZwAllocateVirtualMemory internally...
I wouldn't really recommend it though, except in hacky porting situations where you have no other solution... You're just pushing the problem under the rug, and the code is still broken/wrong (and might still break on future versions of Windows) and isn't fixed on other OS's or platforms...

Trying to look at the bigger picture, it may be time to split the dual-use variable out of existence. Make one for the integer, another for the pointer.

It was fairly common to do that sort of packing when you didn't know the underlying types of data to be moved. You could just make an arbitrary packet and move data around. Normally that meant a union with a bunch of types. The nice thing about when people used unions is that when code migration came around you could convert it into a non-union struct and then use a bunch of testers to shake out the bugs.

Since it is C++ you could do some engineering work if you don't want to split it out. Perhaps create a base class with several explicit conversions that block execution, then overrides with explicit conversions to int or to various pointers. Basically a bunch of lines in your class: explicit operator int() const { Message("wrong conversion used") ... }

But that seems like more work than simply splitting it out to proper classes everywhere and removing all the broken casts in the code.

thanks for the advice. not sure what i will do yet.

i was really hoping that there was a cool casting trick i havent seen yet...

union [...] Also, instead of int, should have used intptr_t

Indeed. The "proper" way of doing this would be to use a union, or alternatively intptr_t.

However, since you do not actually try to store a pointer inside an integer, but rather the opposite, it does not really matter (it's merely a matter of "good style"). Pointers are never smaller than integers (at least I'm not aware of any architecture where that is the case!), and since you store an integer inside a pointer (not the other way around), that will always work, ugly as it is with that cast.

Of course there is that issue with possibly invoking UB with casting from invalid pointers (what's the technobabble they use in the specification for that? "safely-derived pointer" or such?), and another theoretical issue with the nullpointer not necessarily being a zero bit pattern, so depending on what you put into a pointer (e.g. a zero literal, which will convert to the nullpointer), you may still be open to some surprises (but, realistically, on mainstream architectures, it will "just work").


Pointers are never smaller than integers (at least I'm not aware of any architecture where that is the case!), and since you store an integer inside a pointer (not the other way around), that will always work, ugly as it is with that cast.

Not always. Go back a few decades.

My first exposure with C++ was back in 1989 on the PC. The compilers for MS-DOS compatible systems had "near", "far", and "huge" pointers available. Other systems also had their own pointer variants. Both far and huge pointers had more bits than an int. If you attempted to store a regular integer value in a "huge" pointer you would almost certainly get a different value out, since they were re-normalized every time they were assigned in order to maximize their spread for sequential runs.

Today most of us get to enjoy "flat" pointers that are 32 bits and we don't need to specify any fancy modifiers for them.

This topic is closed to new replies.

Advertisement