Я должен отправить некоторые данные по сети, где части пакета не выровнены в байтах.Все пакеты имеют длину 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
плюс флаг, разрешающий анонимные структуры и объединения.