sérialiser un type de données comme vector<u_int8_t> - utiliser reinterpret_cast?
Je n'ai pas trouver quoi que ce soit directement liés à la recherche, de sorte s'il vous plaît pardonnez si c'est un doublon.
Ce que je cherche à faire est de sérialiser des données via une connexion réseau. Mon approche est de convertir tout ce que j'ai besoin de transférer à un std::vector< uint8_t >
et sur le côté de réception de décompresser les données dans les variables appropriées. Ma démarche ressemble à ceci:
template <typename T>
inline void pack (std::vector< uint8_t >& dst, T& data) {
uint8_t * src = static_cast < uint8_t* >(static_cast < void * >(&data));
dst.insert (dst.end (), src, src + sizeof (T));
}
template <typename T>
inline void unpack (vector <uint8_t >& src, int index, T& data) {
copy (&src[index], &src[index + sizeof (T)], &data);
}
Qui je suis en utilisant comme
vector< uint8_t > buffer;
uint32_t foo = 103, bar = 443;
pack (buff, foo);
pack (buff, bar);
//And on the receive side
uint32_t a = 0, b = 0;
size_t offset = 0;
unpack (buffer, offset, a);
offset += sizeof (a);
unpack (buffer, offset, b);
Ma préoccupation est la
uint8_t * src = static_cast < uint8_t* >(static_cast < void * >(&data));
ligne (ce que je comprends à faire la même chose que reinterpret_cast
). Est-il un meilleur moyen pour y parvenir sans le double cast?
Mon approche naïve était simplement utiliser static_cast< uint8_t* >(&data)
qui a échoué. J'ai été dit dans le passé que reinterpret_cast
est mauvais. Donc je voudrais l'éviter (ou de la construction, j'ai actuellement), si possible.
Bien sûr, il y a toujours uint8_t * src = (uint8_t *)(&data)
.
Suggestions?
Vous devez vous connecter pour publier un commentaire.
Ma suggestion est d'ignorer tous les gens qui vous disent que
reinterpret_cast
est mauvais. Ils vous disent qu'il est mauvais, parce que c'est généralement pas une bonne pratique pour prendre la carte mémoire d'un type et de prétendre que c'est un autre type. Mais dans ce cas, c'est exactement ce que vous voulez faire, comme l'ensemble de votre but est de transmettre la mémoire de la carte comme une série d'octets.Il est beaucoup mieux que d'utiliser un double-
static_cast
, comme il entièrement de détails, le fait que vous prenez un type et le dessein de faire semblant de croire que c'est autre chose. Cette situation est exactement ce quereinterpret_cast
est pour, d'esquive et de l'utiliser avec un pointeur void intermédiaire est tout simplement la dissimulation de votre sens avec aucun avantage.Aussi, je suis sûr que vous êtes conscient de cela, mais attention pour les pointeurs en T.
Votre situation est exactement ce que
reinterpret_cast
est pour, c'est plus simple qu'un doublestatic_cast
et documents clairement ce que vous faites.Juste pour être sûr, vous pouvez utiliser
unsigned char
au lieu deuint8_t
:reinterpret_cast
àunsigned char *
et puis déréférencement du pointeur résultant est sûr et portable et est explicitement autorisée par [de base.lval] §3.10/10faire
reinterpret_cast
àstd::uint8_t *
et puis déréférencement du pointeur résultant est une violation de la stricte aliasing règle et est un comportement indéfini sistd::uint8_t
est mis en œuvre tel que prorogé type entier non signé.Si elle existe,
uint8_t
doit toujours avoir la même largeur queunsigned char
. Cependant, il n'a pas besoin d'être du même type; il peut être distinct étendu de type entier. Il a aussi besoin de ne pas avoir la même représentation queunsigned char
(voir Quand est u_int8_t ≠ unsigned char?).(Ce n'est pas tout à fait hypothétique: faire
[u]int8_t
une prolongation de type integer permet des optimisations agressives)Si vous voulez vraiment
uint8_t
, vous pouvez ajouter un:de sorte que le code ne compile pas sur les plates-formes sur lesquelles il en résulterait un comportement indéfini.
static_cast
s et en particulier les avertissements sur leuint8_t
. J'ai lu un post de ce genre, peut-être même le même le, dans le passé - et rapidement dû faire beaucoup des/uint8_t/unsigned char/g
😉Vous pouvez vous débarrasser de l'un cast en exploitant le fait que le pointeur peut être implicitement converti à
void*
. Aussi, vous voudrez peut-être ajouter un peu deconst
:Vous pouvez ajouter au moment de la compilation vérifier
T
être un POD, pas destruct,
et pas de pointeur.Cependant, l'interprétation de certains de l'objet de la mémoire au niveau octet n'est jamais va être enregistrer, période. Si vous devez le faire, faites-le alors dans un joli emballage (comme vous l'avez fait), et d'obtenir sur elle. Lorsque vous port de plate-forme/compilateur, ont un oeil sur ces choses.
const
mais, gommés par souci de concision. Je n'ai pas, cependant, avoir le chèque de pointeur et/ou structure. Il est utilisé uniquement par moi-même, mais il serait probablement plus sûr d'ajouter ces contrôles pour être sûr. Merci.Vous ne faites pas tout réel encodage ici, vous êtes tout simplement la copie de la crue de la représentation des données de la mémoire dans un tableau d'octets, puis envoyer sur le réseau. Qui ne va pas au travail. Voici un exemple rapide pour lesquelles:
Ce qui se passe lorsque vous utilisez votre méthode pour envoyer un
B
sur le réseau? Le destinataire reçoitp_a
, l'adresse de certainsA
objet sur votre machine, mais cet objet n'est pas sur leur machine. Et même si vous avez envoyé laA
objet de trop, il ne serait pas à la même adresse. Il n'y a aucun moyen qui ne peut fonctionner que si vous suffit d'envoyer le rawB
struct. Et c'est même pas envisagé plus de questions subtiles comme boutisme et de la représentation à virgule flottante qui peut affecter la transmission de ces types simples commeint
etdouble
.Ce que vous êtes en train de faire maintenant est fondamentalement pas différent que de simplement jeter à
uint8_t*
quant à savoir si ça va fonctionner ou pas (et ça ne marchera pas, sauf pour la plupart des cas triviaux).Ce que vous devez faire est de mettre au point une méthode de sérialisation. La sérialisation entend de tout moyen de résoudre ce genre de problème: comment obtenir des objets en mémoire sur le réseau sous une forme telle qu'ils peuvent être utilement reconstruit de l'autre côté. C'est un problème délicat, mais il est un fait bien connu et maintes fois résolu le problème. Voici un bon point de départ pour la lecture: http://www.parashift.com/c++-faq-lite/serialization.html
reinterpret_cast
(ou similaire) - je vais renommer pour être plus spécifique. Je suis au courant des subtilités dans le transfert de données et, en interne, tout a un pack/unpack qui consiste essentiellement à ne ce que je décris au-dessus de ses propres données.