QGraphicsItem* conversion into long using reinterpret_cast
-
I am creating test cases for my code. For that, I am capturing my code ( functions ) in Capture Mode and in Run mode I am runnig that captured code. And if both are giving me same output, then my test case is pass otherwise it is fail.
Capture code (which is already written ) does not accept non primitive data types.
Means if some function has Non primitive data types as arguments, then it will not accept. So I need to write a capture function in such a way that it's argument will be primitive data types.So the functions which has non primitive data types as arguments, I am converting them into primitive data type and then after capturing, again converting into it's original type using reinterpret_cast.
Let us say I have following function :SelectObject() { QGraphicsItem* currentSelectedItem = _scene->itemAt( some location ); long currentItem = reinterpret_cast<long>(currentSelectedItem); SelectObjectCapture(currentItem); } SelectObjectCapture(long currentSelectedItem) { CAPTURE FUNCTION(SelectObjectCapture,currentSelectedItem); QGraphicsItem* currentItem = reinterpret_cast<QGraphicsItem*>(currentSelectedItem); // then using currentItem in code }
But this currentItem is leading to crash in RUN MODE. It perfectly works fine in Capture mode or without any mode.
Am I correct in reinterpret_cast ? -
Am I correct in reinterpret_cast ?
No, What you're doing is dangerous and depends on the platform, toolchain and OS.
For example on 64bit WindowsQGraphicsItem*
is 8 bytes in size andlong
is 4 bytes, so you're truncating half of the value. Casting it back you're only recovering lower half of the pointer, which obviously is wrong.It might just so happen that your pointer has a value with non-zero bits only in the lower half of it and then it will accidentally work, but that's depending on your memory allocator and the OS, so nothing you should rely on.
Also, purely from language standpoint, casting a pointer to an integer via reinterpret_cast is implementation defined, meaning you're relying entirely on your compiler vendor for what happens, and you're outside of the standard C++.
I don't know what those modes you mentioned are, but when doing bit casting the least you have to do is make sure the types have the same size (and probably alignment too), otherwise you're just guessing. Prefer types that have defined sizes like
uint64_t
, instead of platform and toolset dependent likelong
, which will vary. Also a pointer is a pretty primitive type, so you should probably have support for it. If you insist on using integers to store pointers there'suintptr_t
that is an integer of a size that can hold a pointer to void. I'll just mention that on some platforms pointers to different types can have different sizes too i.e.sizeof(char*) != sizeof(int*)
, so, although unlikely that it affects you, what you're doing is a risky business. -
Hi @tushu, I'm not really sure, but there's a few things I can suggest here.
First off, depending on the platform,
long
and pointers aren't necessarily the same size. At the very least, I would add something like:static_assert(sizeof(void *) == sizeof(long), "size not the same");
Though I would expect a good compiler to already warn on one of the
reinterpret_cast
lines if the sizes didn't match.Next, I would suggest, instead of using
long
as your primitive type, try using eitherintptr_t
or qintptr. Both should be guaranteed to be the right size I think.Finally, a type-safe might be to use a
QHash
orQMap
to map your primitive to the pointer instead. Something like (pseudo-code only):QHash<int, QGraphicsItem*> capturedItems; SelectObject() { static int itemIndex = 0; capturedItems.insert(itemIndex, _scene->itemAt( some location )); SelectObjectCapture(itemIndex++); } SelectObjectCapture(long currentSelectedItem) { CAPTURE FUNCTION(SelectObjectCapture,currentSelectedItem); QGraphicsItem* currentItem = capturedItems.take(currentSelectedItem); // then using currentItem in code }
But its also very possible that the "crash" is to do with the pointer's lifetime management, rather than the re-castings (ie the pointer might be the correct value, but the item it points to might have been destroyed already). I would strongly suggest logging to pointer values, then running in a debugger to see exactly where the crash happens (it won't be on the
reinterpret_cast
itself, but something soon after).Cheers.
-
Am I correct in reinterpret_cast ?
No, What you're doing is dangerous and depends on the platform, toolchain and OS.
For example on 64bit WindowsQGraphicsItem*
is 8 bytes in size andlong
is 4 bytes, so you're truncating half of the value. Casting it back you're only recovering lower half of the pointer, which obviously is wrong.It might just so happen that your pointer has a value with non-zero bits only in the lower half of it and then it will accidentally work, but that's depending on your memory allocator and the OS, so nothing you should rely on.
Also, purely from language standpoint, casting a pointer to an integer via reinterpret_cast is implementation defined, meaning you're relying entirely on your compiler vendor for what happens, and you're outside of the standard C++.
I don't know what those modes you mentioned are, but when doing bit casting the least you have to do is make sure the types have the same size (and probably alignment too), otherwise you're just guessing. Prefer types that have defined sizes like
uint64_t
, instead of platform and toolset dependent likelong
, which will vary. Also a pointer is a pretty primitive type, so you should probably have support for it. If you insist on using integers to store pointers there'suintptr_t
that is an integer of a size that can hold a pointer to void. I'll just mention that on some platforms pointers to different types can have different sizes too i.e.sizeof(char*) != sizeof(int*)
, so, although unlikely that it affects you, what you're doing is a risky business.@Chris-Kawa said in QGraphicsItem* conversion into long using reinterpret_cast:
on some platforms pointers to different types can have different sizes too i.e.
sizeof(char*) != sizeof(int*)
OMG! Reference/example please? This obviously implies they can have different values, so a pointer to one cannot be exchanged with a pointer to the other. Which I would have thought was enough to break much code where you have to swap between these pointer types to do low-level functions. For example how would
memcpy()
work? How wouldmalloc()
return a pointer which can point to any type? -
@Chris-Kawa said in QGraphicsItem* conversion into long using reinterpret_cast:
on some platforms pointers to different types can have different sizes too i.e.
sizeof(char*) != sizeof(int*)
OMG! Reference/example please? This obviously implies they can have different values, so a pointer to one cannot be exchanged with a pointer to the other. Which I would have thought was enough to break much code where you have to swap between these pointer types to do low-level functions. For example how would
memcpy()
work? How wouldmalloc()
return a pointer which can point to any type?@JonB said:
OMG! Reference/example please?
It's mostly the old stuff, especially the ones with segmented memory models. Some examples are listed here: comp.lang.c FAQ. Btw. there are also examples of null pointer not being 0 on some platforms. Using
nullptr
constant is important :)For example how would memcpy() work? How would malloc() return a pointer which can point to any type?
These functions operate on
void*
, which has a specific size, so there's no problem. Now how would you use a pointer of different size with them? I don't know. I've never worked with any platform like that. -
@JonB said:
OMG! Reference/example please?
It's mostly the old stuff, especially the ones with segmented memory models. Some examples are listed here: comp.lang.c FAQ. Btw. there are also examples of null pointer not being 0 on some platforms. Using
nullptr
constant is important :)For example how would memcpy() work? How would malloc() return a pointer which can point to any type?
These functions operate on
void*
, which has a specific size, so there's no problem. Now how would you use a pointer of different size with them? I don't know. I've never worked with any platform like that.@Chris-Kawa said in QGraphicsItem* conversion into long using reinterpret_cast:
These functions operate on void*
They do indeed, but how do they actually get implemented? If you want to make e.g. a "simple"
memcpy()
at some point you have to cast thatvoid *
to achar *
so you can access the bytes which are there, and then according to you we have a problem because they may not be the same size/convertible :) My question was purely out of interest. -
@Chris-Kawa said in QGraphicsItem* conversion into long using reinterpret_cast:
These functions operate on void*
They do indeed, but how do they actually get implemented? If you want to make e.g. a "simple"
memcpy()
at some point you have to cast thatvoid *
to achar *
so you can access the bytes which are there, and then according to you we have a problem because they may not be the same size/convertible :) My question was purely out of interest.@JonB I'm purely speculating but I'd imagine the compiler for that platform would have some dedicated "variants" of memcpy for these cases. Assuming of course that copying between these types even makes sense. They could just be unrelated. Or if one pointer is something like [segment, offset] and the other is just offset maybe there's some additional function that sets a "base segment" and then memcpy just works on offsets in that segment, i.e. the conversion to that void* would be lossy. A comment here seems to suggest that could be the case.
Idk, there's probably a hefty manual for any such unusual platform on how to do these things. I often write code that needs to be portable across different PCs and game consoles and there are some quirky differences there, but not to that degree. It would be a fun challenge to write portable code for machines where even pointers differ :)