0

So, I've seen this thread Type casting struct to integer c++ about how to cast between integers and structs (bitfields) and undoubtly, writing a proper conversion function or overloading the relevant casting operators is the way to go for any cases where there is an operating system involved. However, when writing firmware for a small embedded system where only one flash image is run, the case might be different insofar, as security isn't so much of a concern while performance is. Since I can test whether the code works properly (meaning the bits of a bitfield are arranged the way I would expect them to be) each time when compiling my code, the answer might be different here.

So, my question is, whether there is a 'proper' way to convert between bitfield and unsigned int that does compile to no operations in g++ (maybe shifts will get optimised away when the compiler knows the bits are arranged correctly in memory).

This is an excerpt from the original question:

struct {
    int part1 : 10;
    int part2 : 6;
    int part3 : 16;
} word;

I can then set part2 to be equal to whatever value is requested, and set the other parts as 0.

word.part1 = 0; 
word.part2 = 9;
word.part3 = 0;

I now want to take that struct, and convert it into a single 32 bit integer. I do have it compiling by forcing the casting, but it does not seem like a very elegant or secure way of converting the data.

int x = *reinterpret_cast<int*>(&word);

EDIT: Now, quite some time later, I have learned some things:

1) Type punning (changing the interpretation of data) by means of pointer casting is, undefined behaviour since C99 and C++98. These language changes introduced strict aliasing rules (They allow the compiler to reason that data is only accessed through pointers of compatible type) to allow for better optimisations. In effect, the compiler will not need to keep the ordering between accesses (or do the off-type access at all). For most cases, this does not seem to present a [immediate] problem, but when using higher optimisation settings (for gcc that is -O which includes -fstrict-aliasing) this will become a problem. For examples see https://blog.regehr.org/archives/959

2) Using unions for type punning also seems to involve undefined behaviour in C++ but not C (See https://stackoverflow.com/a/25672839/4360539), however GCC (and probably others) does explicitly allow it: (See https://gcc.gnu.org/bugs/#nonbugs).

3) The only really reliable way of doing type punning in C++ seems to be using memcpy to copy the data to a new location and perform whatever is to be done and then to use another memcpy to return the changes. I did read somewhere on SO, that GCC (or most compilers probably) should be able to optimise the memcpy to a mere register copy for register-sized data types, but I cannot find it again.

So probably the best thing to do here is to use the union if you can be sure the code is compiled by a compiler supporting type punning through a union. For the other cases, further investigation would be needed how the compiler treats bigger data structures and memcpy and if this really involves copying back and forth, probably sticking with bitwise operations is the best idea.

mox
  • 141
  • 2
  • 8
  • 1
    Why don't you use a union as the answer to the linked question suggests? – mattnewport Jul 27 '15 at 17:24
  • I guess low level knowledge and system dependency is part of firmware writing - drop elegance, –  Jul 27 '15 at 17:27
  • I hoped for another solution more along the lines of casting instead of a union. I used a class-like union (didn't even know those existed) now. – mox Jul 28 '15 at 16:57

2 Answers2

3
union {
    struct {
        int part1: 10;
        int part2: 6;
        int part3: 16;
    } parts;
    int whole;
} word;

Then just use word.whole.

StenSoft
  • 9,369
  • 25
  • 30
  • Notes: - Unnamed structs are non-standard (gcc accepts them, but with -Wpedantic issues a warning). - Accessing both parts of the union is undefined behaviour. But then, the problem itself is undefined behaviour to begin with, so that's no point. In the end it works and it makes it relatively clear to any later readers, that there is a hack involved here. Originally I had hoped for a solution without the union (I don't like unions that much), but since none came up, I guess there is none. So, I'll accept this answer. Thank You! – mox Jul 28 '15 at 16:50
0

I had the same problem. I am guessing this is not very relevant today. But this is how I solved it:

#include <iostream>

struct PACKED{
    int x:10;
    int y:10;
    int z:12;

    PACKED operator=(int num )
    {
        *( int* )this = num;
        return *this;
    }

    operator int()
    {
        int *x;
        x = (int*)this;
        return *x;
    }
} __attribute__((packed));

int main(void) {
    std::cout << "size: " << sizeof(PACKED) << std::endl;

    PACKED bf;
    bf = 0xFFF00000;
    std::cout << "Values ( x, y, z ) = " << bf.x << " " << bf.y << " " << bf.z << std::endl;

    int testint;
    testint = bf;
    std::cout << "As integer: " << testint << std::endl;
    return 0;
}

This now fits on a int, and is assignable by standard ints. However I do not know how portable this solution is. The output of this is then:

size: 4
Values ( x, y, z ) = 0 0 -1
As integer: -1048576
Kristoffer
  • 456
  • 5
  • 17
  • First: See my edit. Using pointer casting for type punning can and will break that code. Second: If this was legal code, I would prefer using reinterpret_cast. This is C++ after all. However, using the overloaded operators to hide whatever is needed to access the whole struct is a nice feature. – mox Aug 11 '17 at 15:15