Estou recebendo dados de outro host via soquete de rede no formato big endian. Como interpretar os bytes recebidos no formato endian nativo (como obter uma visualização ou reinterpretar esses bytes) sem copiar para uma variável temporária.
#include <iostream>
#include <cstdint>
struct A {
uint16_t msg_id;
// contains many other fields where total size is greater than 4096
void print() const {
// print all the fields of struct A
}
};
struct B {
uint16_t msg_id;
// contains many other fields where total size is greater than 4096
void print() const {
// print all the fields of struct B
}
};
struct C {
uint16_t msg_id;
// contains many other fields where total size is greater than 4096
void print() const {
// print all the fields of struct C
}
};
int main() {
char buff[8192];
while (true) {
// data is received in network byte order (big endian) but my system is little endian
const auto recvd_len = recvfrom(sock_fd, buff, sizeof(buff), 0, nullptr, nullptr);
const uint16_t msg_id = (buff[0] << 8) | (buff[1] & 0xFF);
switch (msg_id) {
case 0x0001: {
// reinterpret the bytes received as struct A, copy elision
const A* a_obj = reinterpret_cast<const A*>(buff);
a_obj->print();
// the above print call works correctly only if my system is big endian but not little endian
}
break;
case 0x0002: {
// reinterpret the bytes received as struct B, copy elision
const B* b_obj = reinterpret_cast<const B*>(buff);
b_obj->print();
// the above print call works correctly only if my system is big endian but not little endian
}
break;
case 0x0003: {
// reinterpret the bytes received as struct C, copy elision
const C* c_obj = reinterpret_cast<const C*>(buff);
c_obj->print();
// the above print call works correctly only if my system is big endian but not little endian
}
break;
default:
break;
}
}
}
Em C++, esse é sempre um comportamento indefinido. É uma violação estrita de alias porque
buff
(após a conversão de array em ponteiro) é um ponteiro para umchar
objeto, mas você está acessando seuprint
membro por meio do typeA
. Isso não é permitido; veja [expr.ref] p9 .buff
também está possivelmente subalinhado e você precisa usá-loalignas(A)
para garantir o alinhamento adequado doA
objeto potencialmente interno.C++23 adicionou uma função perfeita para este caso de uso
std::start_lifetime_as
:Para que isso funcione,
A
é necessário que haja um tipo de classe de vida implícita , o que parece possível no seu caso. Infelizmente, nenhum compilador implementastd::start_lifetime_as
no momento da escrita, então você precisará de uma solução alternativa que seja tecnicamente UB, mas não obviamente:Com
std::launder
, você transforma um ponteiro para umchar
objeto em um ponteiro para umA
objeto no mesmo endereço. Este ainda é um comportamento indefinido se não houver realmente umA
objeto lá, mas você pode assumir que issorecvfrom
coloca umA
lá. Pelo menos, o compilador não pode provar querecvfrom
não colocou umA
ali, então ele fará o que você quiser.Nota sobre conversão Endianness
Tenha em mente que todas essas abordagens assumem que o objeto fornecido já está na ordem de bytes nativa, e não na ordem de bytes da rede. Nenhuma conversão Endianness está sendo realizada aqui.
Para obter valores significativos para os membros dos seus dados, você precisaria corrigir a ordem dos bytes após a reinterpretação (não importa qual abordagem), por exemplo, usando
std::byteswap
para cada membro.