Union initialization in C++ and C
Asked Answered
T

4

39

I have built a working C library, that uses constants, in header files defined as

typedef struct Y {
  union {
    struct bit_field bits;
    uint8_t raw[4];
  } X;
} CardInfo;

static const CardInfo Y_CONSTANT = { .raw = {0, 0, 0, 0 } };

I know that the .raw initializer is C only syntax.

How do I define constants with unions in them in a way such that I can use them in C and C++.

Tab answered 19/7, 2012 at 7:16 Comment(9)
are you sure about the mixed-mode tag?Buchmanism
Isn't C++ initializing the unions by the first element? I.e. static const Y_CONSTANT = {{0,0,0,0}};Trenttrento
@Trenttrento then it gives additional warnings about missing braces.Tab
@Alex - I just tried my code and it built fine with no errors/warnings. Are you sure you are using double-braces? typedef struct Y { union { struct bit_field bits; uint8_t raw[4]; } X; } CardInfo; static const CardInfo Y_CONSTANT = {{0, 0, 0, 0 } };Trenttrento
@Trenttrento ok braces seem to do the trick if you use the right amount of them and the right order, but this limits me then to only being able to initialize ONE choice of the union. never allowing me to init the second one.Tab
That is correct and that is how the language is - you are allowed to initialize the FIRST element of the union and only the first oneTrenttrento
My current solution: Avoid constant structures in mixed header files! Define constants in headers extern and hence just forward the linker symbol. initialize the constants in C. Let one language do the initialization seems to me less complicated.Tab
@Alex - The languages are different. In C++ the bit_field would have a constructor to initialize its members, and you would hardly need the union. When you go for the least common denominator, you are limiting yourself.Schizophrenia
@BoPersson well we have a library written in C because it needs to run on a microcontroller an C++ is not an option there YET. the C++ code is the simulator and the test framework. the alternative would be to write everything in C and I believe that this would be worse. or do you have better suggestions?Tab
A
24

I had the same problem. For C89 the following is true:

With C89-style initializers, structure members must be initialized in the order declared, and only the first member of a union can be initialized

I found this explanation at: Initialization of structures and unions

Analgesic answered 22/11, 2013 at 10:11 Comment(0)
R
7

I believe that C++11 allows you to write your own constructor like so:

union Foo
{
    X x;
    uint8_t raw[sizeof(X)];

    Foo() : raw{} { }
};

This default-initializes a union of type Foo with active member raw, which has all elements zero-initialized. (Before C++11, there was no way to initialize arrays which are not complete objects.)

Reynaud answered 19/7, 2012 at 7:31 Comment(4)
@Alex: No, of course not. But with luck the structure will be layout-compatible with a similarly declared C structure...Reynaud
Ah I see so you want to suggest to use #ifdef __cplusplus on the right places to make that header file includeable. Ah that might get even more fuzzy.Tab
Warning : adding a constructor removes PODness, which is often desirable when messing with unions for memory access.Cristencristi
@Cristencristi C++11 introduced a new term standard-layout class , these are allowed to have constructors; and many cases which required POD in C++03 now only require standard-layout. (POD is now defined as standard-layout plus some other guarantees).Deerhound
T
3

I decided to choose the following path.

  • Do not use .member initialization.
  • do nost use static const struct Foobar initialization of members

Instead declare the global variable:

extern "C" {
  extern const struct Foobar foobar;
}

and initialize it in a global section:

struct Foobar foobar = { 0, 0, 0, 0 };

and instead of bugging the C++ compiler with modern ANSI C99 syntax I let the linker do the work be demangling C symbols.

Tab answered 27/7, 2012 at 1:48 Comment(0)
L
0

This is my example how to make const union in C99 (also works in C++).

#include <stdio.h>
#include <stdint.h>

typedef union{
    uint16_t u16;
    uint8_t u8s[sizeof(uint16_t)];
}uint8_16_t;

int main()
{
    const uint8_16_t val= {.u8s={0xAA, 0xBB}};
    printf("0x%04X", val.u16);
    return 0;
}

out: 0xBBAA

Lineolate answered 15/2 at 7:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.