Remplissage de structure en C ++

Si j’ai une struct en C ++, n’y a-t-il aucun moyen de le lire / écrire en toute sécurité dans un fichier compatible avec plusieurs plates-formes / compilateurs?

Parce que si je comprends bien, chaque compilateur «tamponne» différemment en fonction de la plate-forme cible.

Non, ce n’est pas possible. C’est à cause du manque de standardisation du C ++ au niveau binary .

Don Box écrit (citant son livre Essential COM , chapitre COM As A Better C ++ )

C ++ et portabilité

Une fois la décision prise de dissortingbuer une classe C ++ en tant que DLL, on se trouve confronté à l’une des faiblesses fondamentales du langage C ++ , à savoir l’ absence de standardisation au niveau binary . Bien que le document de travail ISO / ANSI C ++ Draft tente de codifier les programmes à comstackr et les effets sémantiques de leur exécution, il ne tente pas de normaliser le modèle d’exécution binary de C ++ . La première fois que ce problème deviendra évident, c’est lorsqu’un client essaie d’établir une liaison avec la bibliothèque d’importation de la DLL FastSsortingng à partir d’un environnement de développement C ++ autre que celui utilisé pour générer la DLL FastSsortingng.

Le remplissage de structure est effectué différemment par différents compilateurs. Même si vous utilisez le même compilateur, l’alignement du package pour les structures peut être différent en fonction du pack de pragma que vous utilisez.

Non seulement, si vous écrivez deux structures dont les membres sont exactement les mêmes, la seule différence est que l’ordre dans lequel elles sont déclarées est différent, alors la taille de chaque structure peut être (et est souvent) différente.

Par exemple, voir ceci,

 struct A { char c; char d; int i; }; struct B { char c; int i; char d; }; int main() { cout << sizeof(A) << endl; cout << sizeof(B) << endl; } 

Comstackz-le avec gcc-4.3.4 et vous obtenez cette sortie:

 8 12 

C'est-à-dire que les tailles sont différentes même si les deux structures ont les mêmes membres!

Code chez Ideone: http://ideone.com/HGGVl

L'essentiel est que la norme ne parle pas de la manière dont le remplissage doit être fait, et donc les compilateurs sont libres de prendre n'importe quelle décision et vous ne pouvez pas supposer que tous les compilateurs prennent la même décision.

Si vous avez la possibilité de concevoir la structure vous-même, cela devrait être possible. L’idée de base est que vous devez le concevoir pour qu’il n’y ait pas besoin d’y insérer des octets de pad. La deuxième astuce est que vous devez gérer les différences en termes d’endurance.

Je décrirai comment construire la structure en utilisant des scalaires, mais vous devriez pouvoir utiliser des structures nestedes, à condition que vous appliquiez la même conception pour chaque structure incluse.

Premièrement, en C et C ++, l’alignement d’un type ne peut pas dépasser la taille du type. Si c’était le cas, il ne serait pas possible d’allouer de la mémoire en utilisant malloc(N*sizeof(the_type)) .

Disposez la structure en commençant par les plus grands.

  struct { uint64_t alpha; uint32_t beta; uint32_t gamma; uint8_t delta; 

Ensuite, complétez manuellement la structure pour que vous trouviez le plus grand type à la fin:

  uint8_t pad8[3]; // Match uint32_t uint32_t pad32; // Even number of uint32_t } 

La prochaine étape consiste à décider si la structure doit être stockée au format little ou big endian. Le meilleur moyen consiste à “échanger” tous les éléments in situ avant d’écrire ou après avoir lu la structure, si le format de stockage ne correspond pas à celui du système hôte.

Non, il n’y a pas de moyen sûr. En plus du remplissage, vous devez gérer différents ordres de octets et différentes tailles de types intégrés.

Vous devez définir un format de fichier et convertir votre struct vers et depuis ce format. Les bibliothèques de sérialisation (par exemple, boost :: serialization ou les protocolbuffers de Google) peuvent vous aider.

Longue histoire courte, non. Il n’y a pas de moyen indépendant de la plate-forme, conforme à la norme, pour gérer le remplissage.

Le rembourrage est appelé “alignement” dans la norme et il commence à en discuter en 3.9 / 5:

Les types d’object ont des exigences d’alignement (3.9.1, 3.9.2). L’alignement d’un type d’object complet est une valeur entière définie par la mise en œuvre représentant un nombre d’octets; un object est alloué à une adresse qui répond aux exigences d’alignement de son type d’object.

Mais ça continue à partir de là et tourne dans de nombreux coins sombres de la Standard. L’alignement est “défini par l’implémentation”, ce qui signifie qu’il peut être différent selon les différents compilateurs, ou même entre les modèles d’adresse (32 bits / 64 bits) sous le même compilateur.

Sauf si vous avez des exigences de performances vraiment difficiles, vous pouvez envisager de stocker vos données sur disque dans un format différent, comme les chaînes de caractères. De nombreux protocoles hautes performances envoient tout en utilisant des chaînes lorsque le format naturel peut être autre chose. Par exemple, un stream d’échange à faible latence sur lequel j’ai récemment travaillé envoie des dates sous forme de chaînes telles que: “20110321” et les heures sont envoyées de la même manière: “141055.200”. Même si ce stream d’échange envoie 5 millions de messages par seconde tout au long de la journée, ils utilisent toujours des chaînes pour tout, car ils peuvent ainsi éviter les problèmes et autres problèmes.

Vous pourriez utiliser quelque chose comme boost::serialization .