1

I'm a trying to migrate some legacy C code for Embedded targets to C++ for compatibility issues and I am encountering some issues with unions in C++.

in our project we have the following style of union to reduce the amount of RAM usage:

typedef union unionTest
{
    struct
    { uint32_t          nField1:5
    ; uint32_t          nField2:4
    ; uint32_t          nField3:23
    ;}
; uint32_t              nRaw                            ///< Raw data
;} unionTest;

// A global variable for configuration
unionTest myCUnion = {.nField1 = 1, .nField2 = 2, .nField3 = 3 };

This works well, is easy to use and the compiler initializes the bit fields at the correct values at compile time

Then when converting to C++

typedef union unionTest
{
    struct
    { uint32_t          nField1:5
    ; uint32_t          nField2:4
    ; uint32_t          nField3:23
    ;}
; uint32_t              nRaw                            ///< Raw data
;} unionTest;

// Using const saves RAM, one of the reason to switch to C++
const unionTest myCppUnion = {.nField1 = 1, .nField2 = 2, .nField3 = 3 }; // Error

does not work anymore, I have a "too many initializers" error. Is there anyway to keep the C behavior. I know that anonymous structures are prohibited by ISO C++ but changing this has a huge impact on the code base, and was not judged important for now.

And I also tried with a named struct

typedef union unionTest
{
    struct notAnonymous
    { uint32_t          nField1:5
    ; uint32_t          nField2:4
    ; uint32_t          nField3:23
    ;} notAnonymous
; uint32_t              nRaw                            ///< Raw data
;} unionTest;

// Using const saves RAM, one of the reason to switch to C++
const unionTest myCppUnion2 = {.notAnonymous.nField1 = 1, .notAnonymous.nField2 = 2, .notAnonymous.nField3 = 3 }; // expected primary-expression before '.' error
const unionTest myCppUnion3 = { .notAnonymous = { .nField1 = 1, .nField2 = 2, .nField3 = 3 } }; // Compiles

and I can't find a solution that keeps the original C behavior.

I have not yet tested if the solution "myCppUnion3" fills the bitfields correctly, but I a m more interested by finding a solution/workaround for solution "myCppUnion"

If anyone has any leads I'll glady take them !

BaptisteC
  • 95
  • 5
  • 4
    FWIW, using a union like this in C++ is undefined behavior. C++ doesn't do type punning through a union. – NathanOliver Oct 08 '20 at 13:11
  • 3
    `Using const saves RAM, one of the reason to switch to C++` how is using const a reason to switch to c++...? – KamilCuk Oct 08 '20 at 13:34
  • It is one of the reasons, and as far as I understood, it allows to store some "variables" in ROM instead of RAM (and other possibilities). I did not make this decision, so I might have misunderstood – BaptisteC Oct 08 '20 at 13:43
  • I believe you need to use C++2x to use designated initializers. – Lundin Oct 08 '20 at 14:04
  • 4
    And for the love of readable code, please place those semicolons at the place where everyone else in the world place them - at the end of each line. – Lundin Oct 08 '20 at 14:05
  • BTW, in C++ you don't need the `typedef` keyword with `class`, `struct` or `union`. – Thomas Matthews Oct 08 '20 at 16:44

2 Answers2

5

In C++ you need an extra set of braces to reflect that the initializers are in a sub-struct:

const unionTest myCppUnion = { {.nField1 = 1, .nField2 = 2, .nField3 = 3 } };
dbush
  • 205,898
  • 23
  • 218
  • 273
3

I have a "too many initializers" error.

The solution to this is simple. You need a set of braces to explicitly express that you are initialising the nested class within the union:

{ // the union
    { // the struct
        .nField1 = 1, // members of the struct
        .nField2 = 2,
        .nField3 = 3,
    }
};

I know that anonymous structures are prohibited by ISO C++

Indeed, that is another thing that must be fixed for C++. With that fixed, you can use a designated initaliser for the struct as in your example.

Third problem you'll likely encounter is that C++ does not allow reading from an inactive union member, which seems likely to be what you do in C.

Fourth potential problem is that if you ever switch the compiler, you may find that the order of your bitfields changes which may be a problem if your program relies on particular order.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Thanks for the clarification! And as a matter of fact the order is very important, as we also use a similar approach when reading specific bits in hardware registers – BaptisteC Oct 08 '20 at 13:45
  • 1
    @BaptisteC Neither C nor C++ guarantee order of bitfields. Portable way to divide integers into bits is to use shifts and masks. – eerorika Oct 08 '20 at 13:55