2010-03-24 5 views
0

J'ai une fonction void write<typename T>(const T&) qui est mise en œuvre en termes d'écriture de l'objet T à un ostream, et une fonction de correspondance T read<typename T>() qui lit un T à partir d'un flux istream. J'utilise essentiellement iostreams comme format de sérialisation en texte brut, ce qui fonctionne bien pour la plupart des types intégrés, bien que je ne sache pas encore comment gérer efficacement std :: strings. J'aimerais aussi pouvoir écrire une séquence d'objets, par exemple void write<typename T>(const std::vector<T>&) ou un équivalent basé sur un itérateur (bien qu'en pratique, il soit toujours utilisé avec un vecteur). Cependant, si écrire une surcharge qui itère sur les éléments et les écrit est assez facile à faire, cela n'ajoute pas assez d'informations pour permettre à l'opération de lecture correspondante de savoir comment chaque élément est délimité, ce qui est essentiellement le même problème que avoir avec un seul std :: string.La manière la plus simple de mélanger des séquences de types avec des flux internes?

Y a-t-il une seule approche qui peut fonctionner pour tous les types de base et std :: string? Ou peut-être que je peux m'en tirer avec 2 surcharges, une pour les types numériques, et une pour les cordes? (Soit en utilisant différents délimiteurs ou la chaîne en utilisant un mécanisme d'échappement delimiter, peut-être.)

EDIT: J'apprécie la tendance souvent judicieux lorsqu'ils sont confrontés à des questions comme celle-ci est-à-dire « vous ne voulez pas faire » et pour suggérer une meilleure approche, mais j'aimerais vraiment des suggestions qui se rapportent directement à ce que j'ai demandé, plutôt que ce que vous croyez que j'aurais dû demander à la place. :)

Répondre

1

Un cadre de sérialisation polyvalent est dur et les fonctionnalités intégrées de la bibliothèque iostream sont vraiment pas à la hauteur - même traiter avec des cordes est assez difficile de façon satisfaisante. Je vous suggère soit de vous asseoir et de concevoir le framework à partir de zéro, en ignorant les iostreams (qui deviennent alors un détail d'implémentation), ou (plus réaliste) d'utiliser une bibliothèque existante, ou au moins un format existant tel que XML.

+0

Je n'ai pas besoin d'une structure de sérialisation complète dans ce cas, mais seulement de la capacité de lire et d'écrire ces types et séquences individuels de types homogènes. – Kylotan

+0

@Kylotan Si vous voulez aller de l'avant, je m'assoirais et réfléchirais à la façon dont vous allez traiter les cordes, qui sont en fait une sorte de séquence de types homogènes. Vous pouvez vous soucier des tableaux, etc. plus tard. –

+0

Heureusement (ou malheureusement, selon la façon dont vous le regardez) j'ai un cas particulier pour les chaînes simples qui fonctionneront - je peux simplement lire le flux entier et l'utiliser. – Kylotan

0

Fondamentalement, vous devrez créer un format de fichier. Quand vous êtes limité aux built-ins, chaînes et séquences de ceux-ci, vous pouvez utiliser des espaces comme délimiteurs, écrire des chaînes enveloppées dans " (qui échappe à " - et puis \, aussi - se produire dans les flux eux-mêmes), et choisir n'importe quoi Cela n'est pas utilisé pour le streaming des types prédéfinis en tant que délimiteur de séquence. Il peut également être utile de stocker la taille d'une séquence.

Par exemple,

5 1.4 "a string containing \" and \\" { 3 "blah" "blubb" "frgl" } { 2 42 21 }

pourrait être la sérialisation d'un int (5), un float (1.4), une chaîne ("a string containing " and \"), une séquence de 3 chaînes ("blah", "blubb", et "frgl"), et une séquence de 2 int s (42 et 21).

Sinon, vous pouvez faire comme Neil suggère in his comment et traiter des chaînes comme des séquences de caractères:

{ 27 'a' ' ' 's' 't' 'r' 'i' 'n' 'g' ' ' 'c' 'o' 'n' 't' 'a' 'i' 'n' 'i' 'n' 'g' ' ' '"' ' ' 'a' 'n' 'd' ' ' '\' }

+0

Je n'ai pas besoin de mélanger les types au sein d'une opération de lecture ou d'écriture afin que la partie ne soit pas un problème. Chaque opération sait exactement de quel type il s'agit, donc j'ai juste besoin de gérer le problème de longueur variable. Malheureusement cette approche des chaînes ne fonctionnera pas bien pour moi car elle brise la lisibilité humaine et la pousse vers un format binaire arbitraire. – Kylotan

+0

@Kylotan: Eh bien, si j'ai bien compris vos commentaires à Mike et moi, alors c'est encore plus simple, puisque vous n'aurez pas besoin d'envelopper les séquences dans les délimiteurs. Il suffit d'écrire '42 ​​21' (ou' 2 42 21', si vous préférez stocker le nombre d'objets) pour stocker 2 entiers. – sbi

+0

C'est encore délimité, juste par les espaces. Cela fonctionne bien pour les types numériques mais ne s'étend pas aux chaînes qui contiennent souvent des espaces. C'est ce à quoi fait référence le troisième paragraphe de ma question. – Kylotan

0

Si vous voulez éviter échapper des chaînes, vous pouvez voir comment ASN.1 fait des choses. C'est exagéré pour vos besoins déclarés: les chaînes, les types fondamentaux et les tableaux de ces choses, mais le principe est que le flux contient des informations de longueur non ambiguës. Par conséquent, rien ne doit être échappé.

Pour un équivalent très simple, vous pourriez sortir un uint32_t comme « UI4 » suivi de 4 octets de données, un int8_t comme « si1 » suivi de 1 octet de données, un flotteur IEEE comme « f4 », double IEEE comme "f8", et ainsi de suite. Utilisez un modificateur supplémentaire pour les tableaux: "a134ui4" suivi de 536 octets de données. Notez que les longueurs arbitraires doivent être terminées, alors que les longueurs bornées comme le nombre d'octets dans l'entier suivant peuvent être fixées en taille (l'une des raisons pour lesquelles ASN.1 est plus que nécessaire est qu'elle utilise des longueurs arbitraires pour tout). Une chaîne pourrait alors être a<len>ui1 ou une abréviation comme s<len>:. Le lecteur est très simple en effet. Ceci a des inconvénients évidents: la taille et la représentation des types doivent être indépendantes de la plate-forme, et la sortie n'est ni lisible ni particulièrement compressée. Vous pouvez le rendre pour la plupart lisible par un humain, mais avec ASCII au lieu de la représentation binaire des types arithmétiques (attention aux tableaux: vous pouvez vouloir calculer la longueur de tout le tableau avant de le sortir, ou vous pouvez utiliser un séparateur et un terminateur car il n'y a pas besoin d'échappement de caractère), et en ajoutant éventuellement un gros gros séparateur humain-visible, que le désérialiseur ignore. Par exemple, s16:hello, worlds12:||s12:hello, world est considérablement plus facile à lire que s16:hello, worlds12:s12:hello, world. Méfiez-vous en lisant que ce qui ressemble à une séquence de séparateur peut ne pas en être un, et vous devez éviter de tomber dans des pièges comme s5:hello|| au milieu du code signifie qu'il y a une chaîne de 5 caractères: cela peut faire partie de s15:hello||s5:hello||.

Sauf si vous avez des contraintes très strictes sur la taille du code, il est probablement plus facile d'utiliser un sérialiseur généraliste que d'en écrire un spécialisé. Lire du XML simple avec SAX n'est pas difficile. Cela dit, tout le monde et son chien ont écrit "enfin, le sérialiseur/analyseur/tout ce qui nous sauvera jamais coder à la main un sérialiseur/analyseur/quoi que ce soit de nouveau", avec plus ou moins de succès.

0

Vous pouvez utiliser boost::spirit, ce qui simplifie l'analyse des types de base à partir de flux d'entrée arbitraires.

Questions connexes