IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

Faire son propre système de sérialisation.


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Invité
    Invité(e)
    Par défaut Faire son propre système de sérialisation.
    Salut,
    étant donné les problèmes que j'ai eu avec certaines librairies de sérialisation en c++ lors de l'enregistrement des types pour les pointeurs sur des objets de classe dérivées.
    de plus ces librairies sont parfois de grosses dépendances supplémentaire à fournir qui augment considérablement la taille de mon framework!

    J'ai décidé de créer mon propre système de sérialisation d'objets.

    L'idée est de passer en template le type réel du pointeur à une classe de base dont hériteront tout les objets sérializable, afin que après je n'ai plus qu'à faire un downcast et appeler la bonne fonction sérialize lors de l'archivation.

    Donc j'ai fait une classe sérializable, son rôle est de contenir le type réel de l'objet. (c'est à dire celui fourni à l'instanciation et non celui fourni lors de la déclaration de l'objet à sérialiser)

    Et de récupérer le bon pointeur vers la fonction membre sérialize c'est à dire celle de la classe dérivée si la classe est héritée), j'ai donc fait ceci :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    #ifndef SERIALIZATION
    #define SERIALIZATION
    namespace odfaeg {
    template <typename C>
    class Serializable {
    public :
        template <typename A>
        void getSerializeFunc(void(C::**func)(A&)) {
             *func = &C::serialize;
        }
    };
    }
    #endif // SERIALIZATION

    Ensuite j'ai fait deux classes (ITextArchive et OTextArchive) exactement comme certaines librairies le font :
    Et je fais un downcast de la classe de base vers la classe dérivée, au cas ou le type de l'objet à sérialisé et le type de la classe contenant la fonction membre serialize sont différent.

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
     
    #ifndef ODFAEG_ARCHIVE
    #define ODFAEG_ARCHIVE
    #include <vector>
    #include <fstream>
    #include "serialization.h"
    namespace odfaeg {
    class OTextArchive {
    public :
        OTextArchive(std::ofstream& buffer) : buffer(buffer) {
     
        }
        template <typename T>
        operator& (T& data) {
            buffer<<data;
        }
        template <typename T>
        operator& (T* data) {
            buffer<<*data;
        }
        template <typename T>
        operator& (std::vector<T>& data) {
            buffer<<data.size();
            for (unsigned int i = 0; i < data.size(); i++)
                buffer<<data[i];
        }
        template <typename T>
        operator& (std::vector<T*> data) {
            buffer<<data.size();
            for (unsigned int i = 0; i < data.size(); i++)
                buffer<<(*data[i]);
        }
        template <typename T>
        void save (T object, void(T::*func)(OTextArchive&)) {
            (object.*func)(*this);
        }
        template <typename B, typename D>
        void save (B* base, void(D::*func)(OTextArchive&)) {
            (static_cast<D*>(base)->*func)(*this);
        }
        template <typename C>
        void operator<<(Serializable<C>* object) {
            void(C::*func)(OTextArchive&);
            object->getSerializeFunc(&func);
            save(object, func);
        }
    private :
        std::ofstream& buffer;
    };
    class ITextArchive {
    public :
        ITextArchive (std::ifstream& buffer) : buffer(buffer) {
        }
        template <typename T>
        operator& (T& data) {
            buffer>>data;
        }
        template <typename T>
        operator& (T* data) {
            data = new T();
            buffer>>*data;
        }
        template <typename T>
        operator& (std::vector<T>& data) {
            int size;
            buffer>>size;
            for (unsigned int i = 0; i < size; i++)
                buffer>>data[i];
        }
        template <typename T>
        operator& (std::vector<T*> data) {
            unsigned int size;
            buffer>>size;
            for (unsigned int i = 0; i < size; i++) {
                T* vi = new T();
                buffer>>(*vi);
                data.push_back(vi);
            }
        }
        template <typename T>
        void load (T object, void(T::*func)(ITextArchive&)) {
            (object.*func)(*this);
        }
        template <typename B, typename D>
        void load (B* base, void(D::*func)(ITextArchive&)) {
            (static_cast<D*>(base)->*func)(*this);
        }
        template <typename C>
        void operator>>(Serializable<C>* object) {
            void(C::*func)(ITextArchive&);
            object->getSerializeFunc(&func);
            load(object, func);
        }
    private :
        std::ifstream& buffer;
    };
    }
    #endif // ODFAEG_ARCHIVE

    Lors de la déclaration de ma classe de base, je dois passer en paramètre template le type réel de l'objet.

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    class BaseObjet : public odfaeg::Serializable<D> {
    public :
        BaseObjet(std::string name) {
            this->name = name;
        }
        template <typename Archive>
        void serialize (Archive & ar) {
            ar & name;
        }
        virtual std::string getName() {
            return name;
        }
    private:
        std::string name;
     
    };
    class ObjetToutBete : public BaseObjet<ObjetToutBete> {
        public:
        ObjetToutBete(std::string name) : BaseObjet("Base objet de objet tout bete") {
            this->name = name;
        }
        template <typename Archive>
        void serialize (Archive & ar) {
            BaseObjet::serialize(ar);
            ar & name;
        }
        std::string getName() {
            return BaseObjet::getName() + "\n" + name;
        }
    private :
        std::string name;
    };

    Ensuite je dois lors de la déclaration, indiquer que le type réel du pointeur est ObjetToutBete.

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    int main(int argc, char* args[])
    {
        std::ofstream ofs("FichierDeSerialisation");
        BaseObjet<ObjetToutBete>* ob = new ObjetToutBete("un objet tout bete");
        odfaeg::OTextArchive oa(ofs);
        oa<<ob;
        std::ifstream ifs("FichierDeSerialisation");
        odfaeg::ITextArchive ia(ifs);
        ia>>ob;
        std::cout<<ob->getName()<<std::endl;
    }

    Ok ça marche et j'ai bien tout dans le fichier

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Base objet de objet tout beteun objet tout bete
    Alors je me demandais si il n'y aurais pas moyen de faire mieux... (sans utiliser une "registration" de type car ça, ça ne marche pas très bien surtout quand j'ai une classe C qui dérive d'une classe B et ensuite une classe A qui dérive de B)
    Car quand je passe BaseObjet en template ça me renvoie une erreur de compilation :
    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    BaseObjet<BaseObjet>* ob = new BaseObjet("un objet de base");

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
     
    ||=== Build: Debug in ODFAEG-DEMO (compiler: GNU GCC Compiler) ===|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp||In function ‘int main(int, char**)’:|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|81|error: type/value mismatch at argument 1 in template parameter list fortemplate<class D> class BaseObjet’|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|81|error:   expected a type, got ‘BaseObjet’|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|81|error: invalid type in declaration before ‘=’ token|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|81|error: expected type-specifier before ‘BaseObjet’|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|83|error: no match foroperator<<’ (operand types are ‘odfaeg::OTextArchive’ andint*’)|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|83|note: candidates are:|
    /usr/local/include/odfaeg/Core/archive.h|41|note: template<class C> void odfaeg::OTextArchive::operator<<(odfaeg::Serializable<C>*)|
    /usr/local/include/odfaeg/Core/archive.h|41|note:   template argument deduction/substitution failed:|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|83|note:   mismatched types ‘odfaeg::Serializable<C>’ andint’|
    /usr/local/include/odfaeg/Math/matrix3.h|147|note: std::ostream& odfaeg::operator<<(std::ostream&, const odfaeg::Matrix3f&)|
    /usr/local/include/odfaeg/Math/matrix3.h|147|note:   no known conversion for argument 1 from ‘odfaeg::OTextArchive’ to ‘std::ostream& {aka std::basic_ostream<char>&}’|
    /usr/local/include/odfaeg/Math/matrix4.h|165|note: std::ostream& odfaeg::operator<<(std::ostream&, const odfaeg::Matrix4f&)|
    /usr/local/include/odfaeg/Math/matrix4.h|165|note:   no known conversion for argument 1 from ‘odfaeg::OTextArchive’ to ‘std::ostream& {aka std::basic_ostream<char>&}’|
    /usr/local/include/odfaeg/Math/matrix2.h|119|note: std::ostream& odfaeg::operator<<(std::ostream&, const odfaeg::Matrix2f&)|
    /usr/local/include/odfaeg/Math/matrix2.h|119|note:   no known conversion for argument 1 from ‘odfaeg::OTextArchive’ to ‘std::ostream& {aka std::basic_ostream<char>&}’|
    /usr/local/include/odfaeg/Math/vec2f.h|205|note: std::ostream& odfaeg::operator<<(std::ostream&, const odfaeg::Vec2f&)|
    /usr/local/include/odfaeg/Math/vec2f.h|205|note:   no known conversion for argument 1 from ‘odfaeg::OTextArchive’ to ‘std::ostream& {aka std::basic_ostream<char>&}’|
    /usr/local/include/odfaeg/Math/vec4.h|241|note: std::ostream& odfaeg::operator<<(std::ostream&, const odfaeg::Vec3f&)|
    /usr/local/include/odfaeg/Math/vec4.h|241|note:   no known conversion for argument 1 from ‘odfaeg::OTextArchive’ to ‘std::ostream& {aka std::basic_ostream<char>&}’|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|86|error: no match foroperator>>’ (operand types are ‘odfaeg::ITextArchive’ andint*’)|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|86|note: candidates are:|
    /usr/local/include/odfaeg/Core/archive.h|88|note: template<class C> void odfaeg::ITextArchive::operator>>(odfaeg::Serializable<C>*)|
    /usr/local/include/odfaeg/Core/archive.h|88|note:   template argument deduction/substitution failed:|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|86|note:   mismatched types ‘odfaeg::Serializable<C>’ andint’|
    /usr/local/include/odfaeg/Math/matrix4.h|166|note: std::istream& odfaeg::operator>>(std::istream&, odfaeg::Matrix4f&)|
    /usr/local/include/odfaeg/Math/matrix4.h|166|note:   no known conversion for argument 1 from ‘odfaeg::ITextArchive’ to ‘std::istream& {aka std::basic_istream<char>&}’|
    /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|87|error: request for member ‘getName’ in ‘* ob’, which is of non-class type ‘int’|
    ||=== Build failed: 7 error(s), 0 warning(s) (0 minute(s), 4 second(s)) ===|

    Et la seconde chose est que je n'arrive pas à mettre un int dans un std::streambuf et je n'ai pas trouvé beaucoup de documentation là dessus.

    Alors voilà je voudrais savoir comment faire ça.

    Merci d'avance.

  2. #2
    Invité
    Invité(e)
    Par défaut
    Mwai..., l'erreur n'est pas claire, il me dit que BaseObjet n'est pas un type hors que c'est un type...,

  3. #3
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Je n'ai pas lu tout ton code, juste ta description de comment tu comptais faire, et j'ai l'impression que tu es passé à côté des éléments qui rendent boost.serialization un peu complexe certes, mais aussi qui la font marcher...

    Par exemple, dans ton cas, que se passe-t-il si tu sérialise un objet A et un B, mais que tous deux possèdent un pointeur sur un objet C ? L'objet C est-il sérialisé deux fois (c'est peu efficace, mais pas gênant) ? Est-il désérialisé deux fois (là, ça devient gênant, on a 2 objets distincts au lieu d'un seul).

    Autre cas, tu as dans une classe un pointeur sur une classe de base, mais tu ne sais pas à la compilation sur quelle classe dérivée tu pointes vraiment. Comment vas-tu la sérialiser ? La désérialiser ?


    Si tu n'es pas dans ces situations, que tu ne sauves que des objets simples, ne contenant aucun sous-objet par pointeur, et dont tu connais le type à la compilation, alors oui, boost.serialization, c'est une usine à gaz, et tu peux t'en passer sans difficultés. Mais si ce n'est pas le cas, ça risque d'être moins simple.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  4. #4
    Invité
    Invité(e)
    Par défaut
    Je n'ai pas lu tout ton code, juste ta description de comment tu comptais faire, et j'ai l'impression que tu es passé à côté des éléments qui rendent boost.serialization un peu complexe certes, mais aussi qui la font marcher...

    Par exemple, dans ton cas, que se passe-t-il si tu sérialise un objet A et un B, mais que tous deux possèdent un pointeur sur un objet C ? L'objet C est-il sérialisé deux fois (c'est peu efficace, mais pas gênant) ? Est-il désérialisé deux fois (là, ça devient gênant, on a 2 objets distincts au lieu d'un seul).
    Ha là j'avoue que ce cas là je n'y avait pas pensé, je sais que boost possède un système de 'object tracking' afin de tester les adresses des objets sérialisé et de ne pas sérialiser deux fois le même. (Je pense que je vais faire ça aussi)

    Autre cas, tu as dans une classe un pointeur sur une classe de base, mais tu ne sais pas à la compilation sur quelle classe dérivée tu pointes vraiment. Comment vas-tu la sérialiser ? La désérialiser ?
    Là je dois t'avouer que je n'ai pas encore été confronté à ce cas là, mais il y a sûrement moyen d'améliorer ça afin de rendre ma classe BaseObjet non template au cas ou on ne connaîtrait pas le type de l'objet, mais je n'ai pas encore trouvé comment.

  5. #5
    Invité
    Invité(e)
    Par défaut
    Salut :

    Pour la seconde solution (une classe contenant un objet de type BaseObjet dont on ne connait pas le type de l'objet dérivé lors de la déclaration de la classe), le problème se résous simplement :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    template <typename D>
    class ObjetContenantBaseObjet : public Serializable<ObjetContenantBaseObjet<D>> {
    public :
        ObjetContenantBaseObjet(std::string name) {
            bo = new D(name);
        }
        template <typename A>
        void serialize(A& ar) {
               ar & bo;
        }
    private :
        BaseObjet<D>* bo;
    };

    Donc bon oui pour moi boost c'est une vrai usine à gazz maintenant pour la 1ère solution ça il suffit de faire un "objet tracking" et de vérifier si l'objet pointé a déjà été sauvegardé lors de la sérialization et si l'objet pointé à déjà été alloué lors de la désérialisation, bref, je n'y vois rien de très compliqué à vrai dire.

    Le seul inconvénient est que, comme BaseObjet est template je peux pas l'utiliser comme ceci :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    BaseObjet<BaseObjet> *bo = new BaseObjet("....");

    Mais dans mon cas ce n'est pas grave car tout mes classes de bases sont abstraite.
    Dernière modification par Invité ; 29/07/2014 à 17h21.

  6. #6
    Invité
    Invité(e)
    Par défaut
    Et voila ceci devrait réglé le problème avec les pointeurs :

    Dans la classe OTextArchive :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
     template <typename T>
        operator& (T* data) {
            bool found = false;
            for (unsigned int i = 0; i < adresses.size() && !found; i++) {
                if (data == adresses[i]) {
                    found = true;
                }
            }
            if (!found) {
                buffer<<*data;
                adresses.push_back(data);
            }
        }

    Et dans la classe ITextArchive :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
     template <typename T>
        operator& (T* data) {
            bool found = false;
            unsigned long long int address;
            for (unsigned int i = 0; i < adresses.size() && !found; i++) {
                if (data == adresses[i]) {
                    found = true;
                    address = adresses[i];
                }
            }
            if (!found) {
                data = new T();
                adresses.push_back(data);
                buffer>>*data;
            } else {
                data = address;
            }
        }

    Pour le fait que je ne puisse pas écrire ceci :
    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    BaseObjet<BaseObjet>* bo = new BaseObjet<BaseObjet>("...");

    Je pense que je vais faire 2 classes différentes, une qui sérialiser les attributs de BaseObjet et de la classe dérivée, et une autre qui ne sérialise que les attributs de la classe de base.

    De toute façon j'ai quand même besoin de pouvoir stocker mes objets dans une collection par la suite.
    Il me faut donc une classe de base pour stocker les objets et une autre classe de base pour les sérialiser.

Discussions similaires

  1. Créer son propre système de fichiers
    Par L'immortel dans le forum Programmation d'OS
    Réponses: 15
    Dernier message: 15/12/2013, 22h16
  2. Utiliser son propre système d'éclairage
    Par nicoenz dans le forum OpenGL
    Réponses: 6
    Dernier message: 07/05/2007, 16h00
  3. Faire son propre serveur DNS?
    Par Death83 dans le forum Applications
    Réponses: 4
    Dernier message: 16/11/2006, 23h41
  4. [PHP-JS] Comment faire son propre BBcode
    Par Sniperman dans le forum Langage
    Réponses: 4
    Dernier message: 22/10/2006, 17h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo