Is there any safe way to cast from an integer to a structure?
As an example:
struct Colour
{
uint8_t A;
uint8_t R;
uint8_t G;
uint8_t B;
};
And I cast to or from an integer:
uint32_t myInteger
Colour* myColour = reinterpret_cast<Colour*>(&myInteger);
uint32_t* myInteger2 = reinterpret_cast<uint32_t*>(myColour);
If my structure is padded, then this won't work, is there any way to guarantee this works?
I understand this might not be standard, but I'd prefer support for major compilers (Visual Studio and GCC) rather than some bitshifting workaround, which has already been answered here: Type casting struct to integer c++.
reinterpret_cast is a type of casting operator used in C++. It is used to convert a pointer of some data type into a pointer of another data type, even if the data types before and after conversion are different. It does not check if the pointer type and data pointed by the pointer is same or not.
the result of a pointer-to-pointer reinterpret_cast operation can't safely be used for anything other than being cast back to the original pointer type.
The dynamic cast is the only that needs to be "calculated" in run-time. All other casts are calculated in compile-time. The machine code for a static_cast is a fixed function based on the type you are casting FROM and TO. For reinterpret_cast , the machine code can be resolved in compile-time as well.
No. It is a purely compile-time construct. It is very dangerous, because it lets you get away with very wrong conversions.
Given the restrictions given in the comments (only care about VC++ and gcc on Windows and Linux), and assuming you're willing to further restrict that to "running on x86 and possibly ARM", you can probably get by pretty easily by adding a pragma
to ensure against padding in the structure:
#pragma pack(push, 1)
struct Colour
{
uint8_t A;
uint8_t R;
uint8_t G;
uint8_t B;
};
#pragma pack(pop)
Note that if you didn't care about compatibility with VC++, you might want to do this differently (gcc/g++ has an __attribute__(aligned(1))
that might otherwise be preferred).
As far as reinterpret_cast
goes, there's a fairly simple rule: the operand and target type must always be either a pointer or a reference (well, you can pass the name of a glvalue, but what's used is a reference to that object)--the whole idea here is to get something that refers to the original object, but "views" it as if it were a different type, and to do that, you have to pass something that gives access to the operand, not just its value.
If the result you want is a value (rather than a reference or pointer) you can dereference the result, and assign the result of that dereference to your target.
uint32_t value = *reinterpret_cast<uint32_t *>(&some_color_object);
or:
color c = *reinterpret_cast<Color *>(&some_uint32_t);
Given the nature of references, it's possible for some of this to be hidden:
color c = reinterpret_cast<Color &>(some_uint32_t);
Here's a quick bit of test code to do some conversions and test/display the results (using both pointers and references, for whatever that may be worth):
#include <iostream>
#include <cassert>
#pragma pack(push, 1)
struct Colour
{
uint8_t A;
uint8_t R;
uint8_t G;
uint8_t B;
bool operator==(Colour const &e) const {
return A == e.A && R == e.R && G == e.G && B == e.B;
}
friend std::ostream &operator<<(std::ostream &os, Colour const &c) {
return os << std::hex << (int)c.A << "\t" << (int)c.R << "\t" << (int)c.G << "\t" << (int)c.B;
}
};
#pragma pack(pop)
int main() {
Colour c{ 1,2,3,4 };
uint32_t x = *reinterpret_cast<uint32_t *>(&c);
uint32_t y = 0x12345678;
Colour d = *reinterpret_cast<Colour *>(&y);
Colour e = reinterpret_cast<Colour &>(y);
assert(d == e);
std::cout << d << "\n";
}
Do note the restrictions given above though. I've tested this with both VC++ (2015) and g++ (5.3), and I'd guess it'll probably work on other versions of those compilers--but there's not much of anything in the way of guarantees with code like this.
It's also entirely possible that it could break even with those compilers, but on a different CPU. In particular, the alignment requirements for your Colour
and for a uint32_t
could be different, so on a CPU that has alignment requirements, it might not work (and even on an Intel, alignment could affect speed).
I can't say that standard guarantees the size in this case, but it's easy to write a compile time assert that would protect you from UB caused by mismatched sizes, by preventing compilation in case the precondition doesn't hold:
static_assert(sizeof(Colour) == sizeof(uint32_t),
"Size of Colour does not match uint32_t. Ask your provider "
"to port to your platform and tell them that bit shifting "
"wouldn't have been such a bad idea after all.");
However, reinterpret_cast<Colour>(myInteger)
is simply ill-formed and conforming compilers refuse to compile it outright.
Edit: Pontential of having padding is not the only problem with reinterpret_cast<uint32_t*>(&myColour)
. uint32_t
may and likely has higher alignment requirement than Colour
. This cast has undefined behaviour.
Is there any safe way to cast from an integer to a structure?
Yes:
myColour.A = (myInteger >> 0) & 0xff;
myColour.R = (myInteger >> 8) & 0xff;
myColour.G = (myInteger >> 16) & 0xff;
myColour.B = (myInteger >> 24) & 0xff;
rather than some bitshifting workaround.
Oh, well there's still std::memcpy
which is guaranteed to work despite alignment differences, although unlike bit shifting, it does require the assertion of equal sizes to hold.
std::memcpy(&myColour, &myInteger, sizeof myColour);
Also, don't forget that if you intend to share the integer representation of the object to other computers, then don't forget to convert endianness.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With