2017-06-22 6 views
3

J'ai une application qui lit actuellement les données d'un flux (socket, named, pipe, stdin, peu importe) dans un tampon char puis utilise reinterpret_cast pour pointer un Foo * (où Foo est un POD) dans le milieu du tampon et puis travaille avec le contenu du tampon à travers ce pointeur. Maintenant, ceci brise des règles strictes d'aliasing, bien que je doute que cela causerait réellement un problème dans la pratique. Pourtant, existe-t-il une façon acceptée de le faire en Standard C++? Parce que nous sommes potentiellement en train de transférer des centaines de gigaoctets de cette façon, et ne voulons en aucun cas introduire le surcoût de la copie de ces données hors du tampon dans une structure avec memcpy.Existe-t-il un moyen conforme aux normes de faire du protocole IPC sans copie en C++?

Pour être clair, le code ressemble à ceci:

MessageData *msg = new MessageData(); 
while (ipc.we_have_data()) { 
    ipc.read(msg); 
    char *buf = msg->data(); 
    Header *h = reinterpret_cast<Header *>(buf); 
    if (h->tag == 0) { 
     Payload *p = reinterpret_cast<Payload *>(buf + sizeof(Header)); 
     do_stuff_with_payload(p); 
    } else if (h->tag == 1) { 
     // etc... 
    } 
} 

Je me rends compte qu'il peut y avoir des problèmes d'alignement, mais ils ne me regardent pas à ce point. Les données sont produites sur la même plate-forme par le même compilateur, donc il n'y a pas de problèmes avec la disposition des membres de la structure étant différente. Mais, si je comprends bien, cela brise techniquement les règles strictes d'aliasing.

Existe-t-il un moyen efficace de le faire qui ne brise pas les règles strictes d'alias?

Ou est-ce que je me trompe complètement, et c'est très bien par ces règles?

Si oui, pourquoi?

Modifier: Un commentaire supprimé pointait vers ce definition of the aliasing rules qui indique que char * obtient un laissez-passer gratuit. Donc, mon exemple ne brise pas les règles strictes d'aliasing. Quelqu'un connaît-il la bonne section de la norme pour cela?

Répondre

1

Malheureusement, la norme n'est pas très conviviale pour traiter des données structurées lues dans un tampon de caractères. Seul le contraire est autorisé: si vous savez que vous allez lire un objet POD, vous pouvez en créer un et passer son adresse convertie en un pointeur char à n'importe quelle fonction qui sera capable de le remplir avec des données réelles, puis l'utiliser normalement.

Le laissez-passer fourni par char * ne permet de traiter un objet à un niveau octet, mais la règle stricte de aliasing interdit normalement de décider qu'un char buffer contient en fait un objet. Quoi qu'il en soit, le plus grand risque serait un problème d'alignement. Pour le reste, des implémentations spécifiques de compilateurs sont parfaitement autorisées à ignorer la règle stricte d'aliasing. Ce qui se passe alors n'est pas défini par la norme mais peut être parfaitement défini par un compilateur à condition de lui passer le drapeau approprié.Votre programme peut alors rompre avec un autre compilateur ou une configuration différente du même compilateur, ce qui pose des problèmes de portabilité, mais cela peut être acceptable à condition qu'il soit clairement documenté - et vous êtes sûr qu'aucun problème d'alignement ne peut survenir ...

+0

Les problèmes d'alignement peuvent souvent être traités efficacement. Traiter le crénelage nécessite soit d'utiliser un compilateur qui peut être utilisé pour reconnaître certaines constructions d'aliasing évidentes, écrire du code lent et inefficace et espérer que le compilateur peut le transformer en un code sensible que l'on voulait écrire en premier, ou en écrivant les compilateurs n'espéreront pas comment casser. Je ne sais pas pourquoi certains rédacteurs de compilateurs semblent être farouchement opposés à # 1, car la reconnaissance de certaines constructions évidentes semblerait plus facile que d'essayer de faire du code efficace à partir d'un désordre conforme aux normes. – supercat

+0

@supercat - Je ne sais pas pourquoi. Cela semble être quelque chose qui devrait vraiment être ajouté à la norme C++, une façon approuvée de faire certains types d'alias. La capacité de faire des choses comme ça fait partie de la raison pour laquelle les gens utilisent des langages comme C et C++ en premier lieu. – Omnifarious

+0

@Omnifarious: Ce qui rend la situation très triste, c'est que si la norme avait simplement donné aux implémentations la permission de faire des suppositions d'alias quand cela aurait un sens *, et a traité le fait que certaines implémentations pourraient ne pas reconnaître certains types d'aliasing comme étant quelque peu analogue au fait que certaines implémentations peuvent mourir si les programmes imbriquent certains appels de fonctions 30 profonds (ou, d'ailleurs, 3 profonds), mais suggèrent que les implémentations de qualité devraient gérer tous les types d'aliasing sensés étant donné la plate-forme cible et l'application field ... – supercat

0

Puisque vous ciblez (vraisemblablement) une plate-forme intégrée avec des caractéristiques bien connues et un comportement bien défini, alors vous pouvez prendre une décision éclairée pour désactiver l'aliasing strict avec -fno-strict-aliasing. Un comportement indéfini est simplement un comportement non portable qui, s'il était défini, pessimait de manière préventive certains systèmes.

Si vous vraiment se soucient de suivre la norme, en utilisant un syndicat est votre meilleur pari. Vous pouvez essayer C++ en utilisant un type d'union sécurisée spécialisée qui n'effectue aucune allocation ou en utilisant un type de classe span/view qui peut accepter un pointeur vers un tampon char, mais qui enveloppe votre POD- API spécifique.