Reputation: 179
I have a struct with bit-fields (totally 32 bit width) and I have a 32-bit variable. When I try to assign the variable value to my struct, I got an error:
error: conversion from ‘uint32_t {aka unsigned int}’ to non-scalar type ‘main()::CPUID’ requested.
struct CPUIDregs
{
uint32_t EAXBuf;
};
CPUIDregs CPUIDregsoutput;
int main () {
struct CPUID
{
uint32_t Stepping : 4;
uint32_t Model : 4;
uint32_t FamilyID : 4;
uint32_t Type : 2;
uint32_t Reserved1 : 2;
uint32_t ExtendedModel : 4;
uint32_t ExtendedFamilyID : 8;
uint32_t Reserved2 : 4;
};
CPUID CPUIDoutput = CPUIDregsoutput.EAXBuf;
Do you have any idea how to do it in the shortest way? Thanks
P.S. Of course I have more appropriate value of EAX in real code, but I guess it doesn't affect here.
Upvotes: 13
Views: 22522
Reputation: 1
I wanted to add a couple of things here in case someone needs the information. As Shahbaz has pointed out, there is one way of SAFELY doing this. Here are the issues with the other ones I see.
The union has the problem of endian-ness. When you have a uint32_t value assigned to a bitfield, big endian will have your bits in one order while little endian will store your bytes of bits in reverse order. When you think you are assigning it one value, you could instead be assigning wrong values to wrong bits. This is therfore NOT portable code.
When you change the pointer type of anything on the LHS of the assignment, assume you are doing something wrong. This is my mantra. Sure, it works, here. Again, this becomes dependent on endian-ness, as well as compiler settings and memory allocation. I can remap any chunk of memory into a different structure, but if I'm doing that then how do I ensure that the 2 structures are of the same size and order? What if down the road you decide to optimize your code and make one a uint16_t, or reorder the bits on a structure? This is a maintenance NIGHTMARE. Just... don't do it. If not for yourself, then for the next individual who is going to have to maintain your code.
Hope this helps explain some of the answers that get the job done but aren't the way to do it.
Upvotes: 0
Reputation: 47523
You should never rely on how the compiler lays out your structure in memory. There are ways to do what you want with a single assignment, but I will neither recommend nor tell you.
The best way to do the assignment would be the following:
static inline void to_id(struct CPUid *id, uint32_t value)
{
id->Stepping = value & 0xf;
id->Model = value >> 4 & 0xf;
id->FamilyID = value >> 8 & 0xf;
id->Type = value >> 12 & 0x3;
id->Reserved1 = value >> 14 & 0x3;
id->ExtendedModel = value >> 16 & 0xf;
id->ExtendedFamilyID = value >> 20 & 0xff;
id->Reserved2 = value >> 28 & 0xf;
}
And the opposite
static inline uint32_t from_id(struct CPUid *id)
{
return id->Stepping
| id->Model << 4
| id->FamilyID << 8
| id->Type << 12
| id->Reserved1 << 14
| id->ExtendedModel << 16
| id->ExtendedFamilyID << 20
| id->Reserved2 << 28;
}
Upvotes: 20
Reputation: 121
Use a union.
union foo {
struct {
uint8_t a : 4;
uint8_t b : 4;
uint8_t c : 4;
uint8_t d : 4;
uint16_t e;
};
uint32_t allfields;
};
int main(void) {
union foo a;
a.allfields = 0;
a.b = 3;
return 0;
}
Upvotes: 12
Reputation: 179
Just if somebody´s interested, I´ve got a better solution for my own question:
*(reinterpret_cast<uint32_t *> (&CPUIDoutput)) = CPUIDregsoutput.EAXBuf;
Upvotes: 2
Reputation: 399833
These are struct members, so you need to assign directly do them, or make sure the RHS of your assignment is a value of type CPUID
. Not sure why you expect to be able to assign to the struct from an integer.
The facts that the struct contains bitfields, and that the sum of the bits happens to be the same as the number of bits in the integer you're trying to assign, mean nothing. They're still not compatible types, for assignment purposes.
If this was too vague, consider showing more/better code.
Upvotes: 1