37

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++.

Mateusz Piotrowski
  • 8,029
  • 10
  • 53
  • 79
Alexander Oh
  • 24,223
  • 14
  • 73
  • 76
  • 1
    are you sure about the mixed-mode tag? – Sebastian Mach Jul 19 '12 at 07:17
  • 2
    Isn't C++ initializing the `union`s by the first element? I.e. `static const Y_CONSTANT = {{0,0,0,0}};` – YePhIcK Jul 19 '12 at 07:19
  • @YePhIcK then it gives additional warnings about missing braces. – Alexander Oh Jul 19 '12 at 07:22
  • @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 } }; – YePhIcK Jul 19 '12 at 07:31
  • @YePhIcK 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. – Alexander Oh Jul 19 '12 at 07:31
  • 1
    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 one – YePhIcK Jul 19 '12 at 07:32
  • 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. – Alexander Oh Jul 19 '12 at 08:19
  • @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. – Bo Persson Jul 19 '12 at 10:44
  • @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? – Alexander Oh Jul 19 '12 at 11:43

3 Answers3

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

Zimano
  • 1,870
  • 2
  • 23
  • 41
hae
  • 256
  • 2
  • 5
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.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • 1
    @Alex: No, of course not. But with luck the structure will be *layout-compatible* with a similarly declared C structure... – Kerrek SB Jul 19 '12 at 07:35
  • 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. – Alexander Oh Jul 19 '12 at 07:39
  • 3
    Warning : adding a constructor removes PODness, which is often desirable when messing with unions for memory access. – Offirmo Jun 19 '13 at 15:13
  • 3
    @Offirmo 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). – M.M Nov 30 '14 at 23:32
2

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.

Alexander Oh
  • 24,223
  • 14
  • 73
  • 76