Использование побитового или вместо std :: max для определения размера буфера

Я смотрел на некоторый сетевой код C ++. Когда я пошел, чтобы выяснить, насколько большой буфер, я нашел что-то вроде этого:

static const uint32_t MAX_COMMAND_BUFFER =
sizeof(struct S1) | sizeof(struct S2) | sizeof(struct S3) |
sizeof(struct S4) | sizeof(struct S5) | sizeof(struct S6);

Я считаю, что основная идея здесь состоит в том, что буфер может содержать любую из этих 6 структур. Это объявлено во внешней области видимости, поэтому оно доступно для таких вещей, как более поздняя область видимости. uint8_t размер массива.

Обычно здесь я ожидаю увидеть что-либо, используя либо функцию std :: max (), либо третичный оператор для вычисления точного размера наибольшей из этих 6 структур. Я никогда не видел вышеупомянутую идиому раньше, и мне пришлось остановиться, чтобы даже убедить себя, что это сработает.

Так как я новичок в этом, я хотел бы в целом узнать, насколько это обоснованно, и каковы преимущества и недостатки этого.

То, что я вижу, —

про:

  • Это всегда должно дать вам размер по крайней мере размером с ваш самый большой размер структуры. И это все, что вам действительно нужно, если пространство не слишком дорого.
  • Похоже, что он рассчитывается во время компиляции.
  • Он должен работать даже с очень старыми компиляторами C ++ (или даже C).

против:

  • Это может сделать размер почти вдвое больше, чем необходимо.
  • Человеку гораздо труднее в автономном режиме выяснить, насколько большой размер буфера.
  • Это странно / неожиданно.

3

Решение

Это верно в том смысле, что MAX_COMMAND_BUFFER всегда будет по крайней мере таким же большим, как максимум всех размеров этих структур. Это легко проверить — поразрядно |— объединение всех размеров даст вам значение, в котором все те же биты имеют наибольший размер, плюс, возможно, некоторые другие.

Тем не менее, это очень запутанно, и вы должны остановиться и подумать об этом. Кроме того, если у вас есть два размера — 8 и 7, вы получите 15, что, вероятно, не то, что вы хотите. К счастью, std::max имеет constexpr перегрузка, которая принимает initializer_list<T>так что просто используйте это:

static constexpr size_t MAX_COMMAND_BUFFER =
//               ^^^^^^
std::max({sizeof(struct S1), sizeof(struct S2), ..., sizeof(struct S6)});

Если ваш компилятор не поддерживает эту перегрузку, довольно просто написать шаблон переменной функции, чтобы выполнить то же самое.

3

Другие решения

Просто запишите намерение:

#include <algorithm>
struct S1 {};
struct S2 {};
struct S3 {};
int main() {
static const unsigned maximum = std::max({sizeof(S1), sizeof(S2), sizeof(S3) /*, ... */});
}

Там нет «за» в запутывания вещей.

2

Я согласен с тобой, я думаю, что то, как они это сделали, это прикольное и неоптимальное.

То, как я делал подобные вещи, заключалось в том, чтобы создать объединение всех структур, а затем использовать размер объединения …

union Commands {
S1 s1;
S2 s2;
...
S6 s6;
};

sizeof(Commands);

И если вы объедините это со структурой, содержащей заголовок вашего пакета, с последующим объединением …

struct Packet {
Header header;
Commands command;
};

Вы можете установить один указатель на ваш буфер и иметь легкий доступ ко всем вашим данным …

Packet *packet = (Packet)&buf[0];
switch(packet->header.command_type) {
case COMMAND_DO_STUFF:
printf("%d\n", packet->command.s1.stuff);
break;
case COMMAND_QUIT:
..
break;
};
2

Это выглядит довольно странно. Я бы использовал sizeof(union { S1 s1; S2 s2; S3 s3; S4 s4; S5 s5; S6 s6; }), (не испытано)

0