Comment mettre en œuvre la sérialisation en C++
Chaque fois que je me retrouve à avoir besoin de sérialiser des objets dans un programme C++, je revenir à ce genre de modèle:
class Serializable {
public:
static Serializable *deserialize(istream &is) {
int id;
is >> id;
switch(id) {
case EXAMPLE_ID:
return new ExampleClass(is);
//...
}
}
void serialize(ostream &os) {
os << getClassID();
serializeMe(os);
}
protected:
int getClassID()=0;
void serializeMe(ostream &os)=0;
};
Ci-dessus fonctionne très bien dans la pratique. Cependant, j'ai entendu dire que ce genre de commutation sur les Id de classe est mal et un antipattern; ce qui est la norme, OO-façon de gérer la sérialisation en C++?
- que les récents changements ? Je n'ai certainement pas entendu parler de tout.
- Je veux dire cette réponse: stackoverflow.com/a/10332336/1065190
- Ah! Je pense que la section des commentaires sur la réponse elle-même sont probablement le meilleur endroit pour en discuter. En fait, j'ai déjà commencé. Il semble assez ésotérique pour moi, surtout l'idée de la fusion de la sérialisation automatique des getters et les setters (c'est généralement mauvais pour mélanger différents concepts). Me rappelle un certain QT projet... à la fin, vous avez quasi-C++ et vous perdez la portabilité, car vous êtes dépendante de la disponibilité de l'outil qui est censé la métamorphoser en bonne, compilable en C++. Je ne suis pas en retenant mon souffle.
- Il est bon de prendre l'a pris avec la source et simplement recompiler sur la plate-forme cible. Qt a en effet un outil appelé metaobject compilateur qui génère de la métainformation pour votre projet C++.
- double possible de Comment sérialiser en c++?
Vous devez vous connecter pour publier un commentaire.
En utilisant quelque chose comme Stimuler La Sérialisation, tandis que pas une norme, est une (pour la plupart) très bien écrit, la bibliothèque qui fait le gros du travail pour vous.
La dernière fois que j'ai eu à analyser manuellement un prédéfinies de la structure de l'enregistrement avec une nette arbre d'héritage, j'ai fini à l'aide de la modèle de fabrique avec enregistrable classes (c'est à dire à l'Aide d'une carte de clé à un (modèle) créateur de la fonction plutôt que de beaucoup de fonctions de l'interrupteur) pour essayer d'éviter le problème que vous aviez.
MODIFIER
Une base de C++ mise en œuvre d'une fabrique d'objet mentionné dans le paragraphe ci-dessus.
La sérialisation est un sujet délicat en C++...
Question rapide:
Les 2 sont utiles, et ont à leur utilisation.
Coup de pouce.La sérialisation est le plus recommandé bibliothèque de sérialisation habituellement, bien que l'étrange choix de
operator&
qui sérialise ou désérialise en fonction de la const-ness est vraiment un abus de la surcharge d'opérateur pour moi.Pour la messagerie, je préfère suggérer Google Protocole Tampon. Ils offrent une propre syntaxe pour décrire le message et de générer des encodeurs et des décodeurs pour une grande variété de langues. Il y a aussi un autre avantage lorsque la performance est importante: elle permet aux paresseux de la désérialisation (c'est à dire uniquement la partie de la goutte à la fois) par la conception.
De passer
Maintenant, comme pour les détails de mise en œuvre, cela dépend vraiment de ce que vous souhaitez.
tag
+factory
. Il est seulement nécessaire pour les polymorphes de la classe. Et vous aurez besoin d'unfactory
par arbre d'héritage (kind
) puis... le code peut être templatized bien sûr!kind
est donné unid
, unique pour sakind
, et j'ai donc sérialiser lesid
plutôt qu'un pointeur. Certains framework gère aussi longtemps que vous n'avez pas de dépendance circulaire et de sérialiser les objets pointés /référencé en premier.Personnellement, j'ai essayé autant que je peux pour séparer le code de sérialisation /désérialisation du code qui s'exécute la classe. En particulier, j'essaie de l'isoler dans les fichiers source pour que les changements sur cette partie du code n'est pas d'anéantir la compatibilité binaire.
Sur la gestion des versions
J'ai l'habitude d'essayer de garder la sérialisation et la désérialisation d'une version de la proximité. Il est plus facile de vérifier qu'ils sont vraiment symétrique. J'ai aussi essayer de résumé la gestion des versions de manutention directement dans mon sérialisation cadre + un peu d'autres choses, parce que la SEC doit être respecté 🙂
Sur la gestion des erreurs
Pour faciliter la détection des erreurs, j'ai l'habitude d'utiliser une paire de "marqueurs" (spécial octets) pour séparer un objet à partir d'un autre. Il me permet de jeter immédiatement lors de la désérialisation, parce que je peux détecter un problème de désynchronisation des flux (c'est à dire, un peu mangé trop d'octets ou n'a pas mangé suffisamment).
Si vous voulez permissive de la désérialisation, c'est à dire de la désérialisation du reste du flux, même si quelque chose a échoué avant, vous aurez à se déplacer vers octet-comte: chaque objet est précédé par sa octet-comte et de n'en manger tellement octet (et il est prévu de les manger tout). Cette approche est intéressante parce qu'elle permet partielle de la désérialisation: c'est à dire que vous pouvez enregistrer la partie du flux requis pour un objet et une seule désérialiser si nécessaire.
De marquage (votre Id de classe) est utile ici, non pas (seulement) pour la distribution, mais simplement de vérifier que vous êtes en fait la désérialisation le bon type d'objet. Il permet aussi de jolis messages d'erreur.
Voici quelques messages d'erreur /exception que vous pouvez souhaiter:
No version X for object TYPE: only Y and Z
Stream is corrupted: here are the next few bytes BBBBBBBBBBBBBBBBBBB
TYPE (version X) was not completely deserialized
Trying to deserialize a TYPE1 in TYPE2
Remarque qu'aussi loin que je me souviens de la fois
Boost.Serialization
etprotobuf
vraiment aider pour l'erreur/la version de manutention.protobuf
a quelques avantages trop, à cause de sa capacité de nidification messages:La contrepartie est qu'il est plus difficile de gérer le polymorphisme en raison de l'fixe le format du message. Vous devez soigneusement les concevoir pour qu'.
La sérialisation est malheureusement jamais être totalement indolore en C++, au moins pas dans un avenir prévisible, tout simplement parce que le C++ ne manque de la critique pour le langage qui permet la sérialisation possible dans d'autres langues : la réflexion. C'est, si vous créez une classe
Foo
, C++ n'a pas de mécanisme pour inspecter la classe par programmation à l'exécution pour déterminer quel membre de variables qu'il contient.Donc, il n'y a aucun moyen de créer généralisée de la sérialisation des fonctions. D'une façon ou d'une autre, vous avez à mettre en œuvre une spéciale de sérialisation de la fonction pour chaque classe. Coup de pouce.La sérialisation n'est pas différent, tout simplement, il vous offre un cadre commode et une belle série d'outils qui vous aideront à le faire.
La réponse par Yacoby peut être étendu plus loin.
Je crois que la sérialisation peut être mis en œuvre de manière similaire aux langages managés si l'on met en œuvre un système de réflexion.
Pendant des années, nous avons été à l'aide de l'approche automatisée.
J'ai été l'un des réalisateurs de travail C++ post-processeur et de la Réflexion de la bibliothèque: LSDC outil et Linderdaum de Base de Moteur (iObject + RTTI + Linker/Chargeur). Voir la source à http://www.linderdaum.com
La fabrique de classe résumés le processus d'instanciation de classe.
Pour initialiser les membres spécifiques, vous pouvez ajouter un peu intrusif RTTI et de générer automatiquement le chargement/enregistrement des procédures pour eux.
Suppose, vous avez la iObject classe au sommet de votre hiérarchie.
Lorsque vous définissez la ConcreteClass dérivé de iObject, vous utilisez une extension, et le générateur de code de l'outil de produire la liste de sauvegarder/charger les procédures et le code d'enregistrement pour.
Laissez-nous voir le code de cet exemple.
Quelque part dans le cadre que nous avons un vide formel définir
Généré automatiquement le code d'enregistrement serait:
Maintenant, si vous avez le JSON comme hiérarchique script
L'éditeur de liens a pour but de résoudre toutes les classes et propriétés à Enregistrer/Charger des méthodes.
Désolé pour le long post, la mise en œuvre pousse encore plus grande lorsque toutes les erreurs de manipulation est en.
protobuf
fichiers), mais lorsque l'extra pass trashes le fichier et vous vous retrouvez avec une erreur de compilation, le suivi de l'erreur est un cauchemar.Peut-être que je ne suis pas malin, mais je pense que, finalement, le même type de code que vous avez écrit est écrit, tout simplement parce que le C++ n'est pas le moteur d'exécution des mécanismes pour faire quelque chose de différent. La question est de savoir si elle sera écrite sur mesure par un développeur, généré par modèle métaprogrammation (qui est ce que je soupçonne que boost.la sérialisation n'), ou généré via un outil externe comme un compilateur IDL/générateur de code.
La question de savoir laquelle de ces trois mécanismes (et peut-être il y a d'autres possibilités, aussi) est quelque chose qui doit être évalué sur une base par projet.
Je suppose que la chose la plus proche à une norme serait Coup de pouce.La sérialisation. Je voudrais l'entendre et dans quel contexte vous avez appris que la chose à propos de l'Id de classe. Dans le cas de la sérialisation je peux vraiment penser à une autre façon (à moins bien sûr, vous connaissez le type que vous attendez lors de la désérialisation). Et aussi, Une taille unique ne convient pas à tous.