Я пишу программу на C (g ++ compilable), которая имеет дело с множеством различных структур, все из буфера с предопределенным форматом. Формат указывает, какой тип структуры я должен загрузить. Это может быть решено с помощью союзов, но огромная разница в размерах структур заставила меня выбрать конструкцию с пустотой * в ней:
struct msg {
int type;
void * data; /* may be any of the 50 defined structures: @see type */
};
Проблема в том, что мне нужно 2 malloc
звонка и 2 free
. Для меня вызовы функций стоят дорого, а malloc
дорого. Со стороны пользователей было бы здорово просто free
сообщения. Поэтому я изменил определение на:
struct msg {
int type;
uint8_t data[]; /* flexible array member */
};
...
struct msg_10 {
uint32_t flags[4];
...
};
Всякий раз, когда мне нужно десериализовать сообщение, я делаю:
struct msg * deserialize_10(uint8_t * buffer, size_t length) {
struct msg * msg = (struct msg *) malloc(offsetof(struct msg, data) + sizeof(struct msg_10));
struct msg_10 * payload = (__typeof__(payload))msg->data;
/* load payload */
return msg;
}
И чтобы получить член этой структуры:
uint32_t msg10_flags(const struct msg * msg, int i)
{
return ((struct msg_10 *)(msg->data))->flags[i];
}
С этим изменением gcc (и g ++) выдает красивое сообщение warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
.
Я думаю, что это распространенная проблема (но я не нашел здесь ответа) о том, как каким-то эффективным образом представить семейство сообщений в C.
Я понимаю, почему появилось предупреждение, у меня следующие вопросы:
- Возможно ли реализовать что-то подобное без предупреждения или это по своей природе некорректно? (или не является эксклюзивным: P, и я почти убежден, что мне следует провести рефакторинг)
Было бы лучше представлять сообщения, используя что-то вроде следующего кода?
struct msg {
int type;
};
...
struct msg_10 {
struct msg; /* or int type; */
uint32_t flags[4];
...
};
Если да, предостережения? Могу ли я всегда писать и использовать следующее?
struct msg * deserialize_10(uint8_t * buffer, size_t length) {
struct msg_10 * msg = (struct msg_10 *) malloc(sizeof(struct msg_10));
/* load payload */
return (struct msg *)msg;
}
uint32_t msg10_flags(const struct msg * msg, int i) {
const struct msg_10 * msg10 = (const struct msg_10 *) msg;
return msg10->flags[i];
}
Любой другой?
Я забыл сказать, что это работает на низкоуровневых системах и производительность является приоритетом, но, в общем, реальная проблема заключается в том, как обрабатывать эту структуру с несколькими сообщениями. Я могу провести рефакторинг один раз, но изменив реализацию десериализации 50 типов сообщений ...