Aki Suihkonen
Aki Suihkonen

Reputation: 20037

Converting between const struct types in compile time

Given a constant struct in one API, which is to be interpreted as 16 consecutive uint8_t bytes in other API, is there a method in C to make this conversion in compile time:

What I'd like to achieve is something like

 const union {
     struct a {
         uint32_t a;
         uint16_t b;
         uint16_t c;
         uint8_t d[8];
     } a;
     uint8_t b[16];
 } foo = { .a = { 0x12341243, 0x9898, 0x4554,
                { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 } } };

struct from_other_api manifest = {
    .appuuid = foo.b;
    // { foo.b[0], foo.b[1], ...  }
};

This approach, as well the second version in the commented line unfortunately give both an error error: initializer element is not constant, even though this surely looks like a constant.

The business reason is that both the definitions struct from_other_api manifest and the constant memory blob come an API, which is not to be modified. The conversion can be done manually as

struct from_other_api manifest = {
    .appuuid = { 0x43, 0x12, 0x34, 0x12, 0x98, 0x98, 0x54, 0x45,
                 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }
};

but is to be avoided, as this is a regular pattern yelling to be automatized.

Upvotes: 1

Views: 127

Answers (3)

user694733
user694733

Reputation: 16043

In C constant variables cannot be used in constant expressions.

If you can initialize manifest at runtime, you can do it with memcpy as in Snaipes answer.

But if manifest must be initialized at compile time, you may need to (ab)use preprocessor. It won't be too pretty, but it works:

#define ID 0x12341243, 0x9898, 0x4554, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08

#define FOO(nnn)  { .a = FOO2(nnn) }
#define FOO2(a, b, c, d, e, f, g, h, i, j, k) \
    { a, b, c, { d, e, f, g, h, i, j, k } }

#define MAN(nnn) MAN2(nnn)
#define MAN2(a, b, c, d, e, f, g, h, i, j, k) \
    { a >> 0 & 0xFF, a >> 8 & 0xFF, a >> 16 & 0xFF, a >> 24 & 0xFF, \
    b >> 0 & 0xFF, b >> 8 & 0xFF, \
    c >> 0 & 0xFF, c >> 8 & 0xFF, \
    d, e, f, g, h, i, j, k }

const union {
    ...
 } foo = FOO(ID);

struct from_other_api manifest = {
    .appuuid =  MAN(ID)
};

Upvotes: 1

Snaipe
Snaipe

Reputation: 1221

You cannot pass anything other than literals in array initializers. Use memcpy(3) instead:

struct from_other_api manifest = {
    // initialize other members
};
memcpy(manifest.appuuid, foo.b, sizeof (manifest.appuuid));

Upvotes: 1

a_pradhan
a_pradhan

Reputation: 3295

This declaration does not declare a variable.

struct a {
     uint32_t a;
     uint16_t b;
     uint16_t c;
     uint8_t d[8];
 };

To declare a variable named a with the same structure inside the union, use this:

const union {
    struct {
         uint32_t a;
         uint16_t b;
         uint16_t c;
         uint8_t d[8];
     } a; // now a is accessible with a.a, a.b, a.c and a.d[i].
     uint8_t b[16];
} foo = { .a = { 0x12341243, 0x9898, 0x4554,
            { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 } } };

Upvotes: 2

Related Questions