Чистый синтаксис для заполнения структур Bitfield из параметров функции - PullRequest
0 голосов
/ 27 февраля 2019

Я должен отправить некоторые данные по сети, где части пакета не выровнены в байтах.Все пакеты имеют длину 8 байт, и пример типа пакета может выглядеть следующим образом:

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

Итак, первые 5 битов - это поле a, следующие 10 битов - это поле b, а последний бит - это полеc.Теперь мне нужна функция send, которая может отправлять этот и, возможно, другие типы пакетов.Функция должна принимать поля в качестве параметров.Функция низкоуровневой отправки принимает один uint64_t.

Редактировать Как указано в комментариях, читать из raw после записи в * небезопасно1015 *, b или c.Чтобы было понятно: это тоже то, что я хотел бы изменить, но я включил его сверху, потому что объединение используется для всех моих попыток.

Мои требования:

  • Переполнение должно быть обнаружено во время компиляции
  • Синтаксис не должен быть слишком громоздким (субъективно, но я покажу, что я хочу)

Моя первая попытка

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);
}

Сгенерированы предупреждения, которые удовлетворяют моему первому требованию, но мне не нравится синтаксис: фигурные скобки выглядят излишними, а создание структуры сначала вручную громоздко.

С некоторымипредупреждения позволяют мне даже использовать два слоя фигурных скобок, потому что структура вложена в объединение.

Вторая попытка

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

    raw_send(packed.raw);
};

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

Здесь мне нравитсясинтаксис, но предупреждения не генерируются, предположительно из-за потери битовой ширины.

Третья попытка

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
}

Первое использование снова уродливо: слишком много фигурных скобок, а второе -не соmpile, потому что структуры не могут быть неявно созданы с одним значением.

Вопрос

Как я могу реализовать функцию и определение безопасного пакета, чтобы следующий вызов функции компилировал и показывает предупреждение, потому что значение 1000 не умещается в 5 бит.

send(1000u, 2u, true);

На самом деле меня волнует только сайт вызова.Определения функций и объединений могут быть более сложными.

Редактировать 2

Использование переменных для параметров функции must should работа тоже.

uint64_t a, b;
send(a, b, true);  // compiles, but may generate a warning
send(a & 0x1f, b & 0x3ff, true);  // compiles preferably without a warning

Программное обеспечение будет использоваться только на Linux, скомпилировано с gcc или clang с использованием в аренду этих предупреждений: -Wall -Wextra -pedantic плюс флаг, разрешающий анонимные структуры и объединения.

...