mercredi 27 février 2019

Clean syntax for filling Bitfield structs from function parameters

I must send some data over a network where the package parts are not byte aligned. All packages are 8 byte long and an example package type might look like this:

union Packed
{
    struct
    {
        uint64_t a : 5;
        uint64_t b : 10;
        bool c : 1;
    };
    uint64_t raw;
};

So the first 5 bits are field a the next 10 bits are field b and the last bit is field c. Now I need a send function that can send this and possibly other package types. The function should accept the fields as parameters. The low-level send function accepts a single uint64_t.

My requirements are:

  • An overflow should be detected at compile time
  • The syntax should not be too bulky (subjective, but i will show what i want)

My first attempt

void send_struct(const Packed& packed)
{
    raw_send(packed.raw);
}

int main()
{
    send_struct({1000, 2, true});  // conversion changes value

    Packed packed = {1000, 2, true};  // conversion changes value
    send_struct(packed);
}

The warnings are generated which satisfies my first requirement, but i don't like the syntax: The curly braces look superfluous and manually creating a struct first is cumbersome.

With some warnings enable i even have to use two layers of curly braces, because the struct is nested inside the union.

Second attempt

template <typename ...Args>
void send_var(Args... args)
{
    Packed packed {args...};

    raw_send(packed.raw);
};

int main()
{
    send_var(1000u, 2u, true);
}

Here, i like the syntax, but no warnings are generated, presumably because the bit width is lost somewhere.

Third attempt

struct A
{
    uint64_t data : 5;
};

struct B
{
    uint64_t data : 10;
};

void send_separate(A a, B b, bool c)
{
    Packed packed {a.data, b.data, c};

    raw_send(packed.raw);
}

int main()
{
    send_separate({1000u}, {2u}, true);  // conversion changes value
    send_separate(1000u, 2u, true);  // compile error
}

The first usage is ugly again: too many curly braces and the second one does not compile, because the structs cannot be implicitly constructed with a single value.

Question

How can i implement a function (and Package definition) such that the following function call compiles and shows a warning, because the value 1000 does not fit into 5 bit.

send(1000u, 2u, true);

I actually only care about the call site. The function and union definitions may be more complicated.

Using variables for the function parameters must work, too, of course

uint64_t a, b;
send(a, b, true);  // should generate a warning

send(a & 0x1f, b & 0x3ff, true);  // no warnings

The software will be used on linux only, is compiled with gcc or clang using at lease these warnings: -Wall -Wextra -pedantic plus the flag that allows anonymous structs and unions.

Aucun commentaire:

Enregistrer un commentaire