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++

  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 : 49
    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
    Points : 16 213
    Points
    16 213
    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.

  7. #7
    Invité
    Invité(e)
    Par défaut Je pense que mon problème est enfin résolu!
    Bon j'ai enfin trouvé, en fait il y a les objets ostream et istream qui sont les classe de bases de tout les streams d'entrée et de sortie, ça y es maintenant je pense que je peux sérialiser n'importe quel type d'objet ou bien de collection!
    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
    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
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
     
    #ifndef ODFAEG_ARCHIVE
    #define ODFAEG_ARCHIVE
    #include <vector>
    #include <ostream>
    #include <istream>
    #include "serialization.h"
    namespace odfaeg {
    class OTextArchive {
    public :
        OTextArchive(std::ostream& buffer) : buffer(buffer) {
     
        }
        template <typename T>
        operator& (T& data) {
            buffer<<data;
        }
        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);
            }
        }
        template <typename T>
        operator& (std::vector<T>& data) {
            std::size_t size = data.size();
            buffer<<size;
            for (unsigned int i = 0; i < data.size(); i++)
                buffer<<data[i];
        }
        template <typename T>
        operator& (std::vector<T*> data) {
            std::size_t size = data.size();
            buffer<<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&);
            static_cast<C*>(object)->getSerializeFunc(&func);
            save(object, func);
        }
    private :
        std::ostream& buffer;
        std::vector<unsigned long long int> adresses;
    };
    class ITextArchive {
    public :
        ITextArchive (std::istream& buffer) : buffer(buffer) {
        }
        template <typename T>
        operator& (T& data) {
            buffer>>data;
        }
        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) {
                data = new T();
                adresses.push_back(data);
                buffer>>*data;
            }
        }
        template <typename T>
        operator& (std::vector<T>& data) {
            std::size_t size;
            buffer>>size;
            for (unsigned int i = 0; i < size; i++)
                buffer>>data[i];
        }
        template <typename T>
        operator& (std::vector<T*> data) {
            std::size_t 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::istream& buffer;
        std::vector<unsigned long long int> adresses;
    };
    }
    #endif // ODFAEG_ARCHIVE
    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
     
     
    #include "myApplication.h"
    #include "odfaeg/Graphics/renderWindow.h"
    #include "glCheck.h"
    #include "odfaeg/Graphics/window.h"
    #include <fstream>
    #include "odfaeg/Core/archive.h"
    #include "odfaeg/Core/serialization.h"
    template<typename D>
    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 Base : public odfaeg::Serializable<Base> {
    public :
        Base(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;
    };
    template <typename D>
    class ObjetContenant : public odfaeg::Serializable<ObjetContenant<D>> {
        ObjetContenant(std::string name) {
            objet = new D(name);
        }
        template <typename Archive>
        void serialize(Archive & ar) {
            ar & objet;
        }
        BaseObjet<D>* objet;
    };
    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;   
    }

    Maintenant il ne reste plus qu'à tester ça pour toutes mes classes, voir si j'arrive bien à tout sérialiser.

    Si ça marche bah ça aura été tout simple finalement, comparé à l'usine à gaz que m'offre boost.

  8. #8
    Invité
    Invité(e)
    Par défaut
    Bon finalement j'ai trouvé une meilleur méthode qui me permettra de rendre ça compatible avec boost et d'autres librairies, l'idée est d'utiliser des macros qui définissent le type de l'objet qu'utilise la librairie pour sérializer.

    Par exemple si moi j'utilise cet objet pour sérializer qui ne fait rien d'autre que de remplir le tuple avec tout les types d'objets possible pour les objets polymorphics, et qui recherche le bon type à l'exécution :

    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
     
    #ifndef ODFAEG_SERIALIZATION
    #define ODFAEG_SERIALIZATION
    #include "archive.h"
    #include <iostream>
    #include <typeinfo>
    #include "tuple.h"
    #include <tuple>
    namespace odfaeg {
    template <class B, class... D>
    class Serializer {
        public :
        Serializer(B* baseObject) : baseObject(baseObject) {
            fillTuple(derivedClasses);
        }
        template<int... S>
        void fillTuple(std::tuple<D*...> types) {
            fillTuple(typename helper::gens<sizeof...(D)>::type(), types);
        }
        template<int... S>
        void fillTuple(helper::seq<S...>, std::tuple<D*...> params) {
             derivedClasses = std::forward_as_tuple(dynamic_cast<typename std::remove_reference<decltype(std::get<S>(params))>::type> (baseObject)...);
        }
        template <typename Archive>
        void checkType(Archive & ar) {
            serialize(derivedClasses, ar);
        }
        template<int... S, typename Archive>
        void serialize (std::tuple<D*...> types, Archive & ar) {
            //The class doesn't inherits;
            if (sizeof...(D) == 0 && baseObject != nullptr) {
                baseObject->serialize(ar);
            } else if (sizeof...(D) > 0 && baseObject != nullptr) {
                if(!serialize(typename helper::gens<sizeof...(D)>::type(), types, ar))
                    baseObject->serialize(ar);
            }
        }
        template<int... S, typename Archive>
        bool serialize(helper::seq<S...>, std::tuple<D*...> params, Archive & ar)
        {
            if (evaluateType(std::get<S>(params)..., ar)) {
                return true;
            }
            return false;
        }
        template <typename T, typename Archive>
        bool evaluateType (T* type, Archive & ar) {
            if (type != nullptr && typeid(*type) == typeid(*baseObject)) {
                type->serialize(ar);
                return true;
            }
            return false;
        }
        virtual void onSave() {
        }
        virtual void onLoad() {
        }
        B* baseObject;
        std::tuple<D*...> derivedClasses;
    };
    }
    #endif // SERIALIZATION

    Je n'ai qu'à déclarer ce type d'objet à l'aide d'une macro comme ceci :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    #define var(z, O, T...) z = odfaeg::Serializer<T>(O)
    #define decl(x, OBJ, TPS...) var(x, OBJ, TPS)
    #define REGISTER_SERIALIZABLE_OBJECT(NAME, OBJECT, TYPES...)  odfaeg::Serializer<TYPES> decl(NAME, OBJECT, TYPES)

    Ensuite le sérializer :

    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
     
     ObjetToutBete* ocotb = new ObjetContenantObjetToutBete("Objet contenant objet tout bête.");
        ObjetToutBete* otb = new ObjetToutBete("Objet contenant objet tout bête.");
        REGISTER_SERIALIZABLE_OBJECT(s1, ocotb, ObjetToutBete, ObjetContenantObjetToutBete);
        std::ofstream ofs("FichierDeSerialisation");
        {
            odfaeg::OTextArchive oa(ofs);
            oa<<s1;
        }
        ofs.close();
        std::ifstream ifs("FichierDeSerialisation");
        {
            odfaeg::ITextArchive ia(ifs);
            ia>>s1;
        }
        ifs.close();
        return 0;

    Donc plutôt que d'enregistrer les types, j'enregistre les objets.

    La dernière chose à savoir maintenant est quel est le type de l'objet qu'utilise boost pour sérialiser. (En gros la classe qui appelle la méthode template serialize)

  9. #9
    Invité
    Invité(e)
    Par défaut
    Ca y est j'ai trouvé un système qui roxxx!!!
    Je ne voyais pas très bien comment m'y prendre avec les macros mais voilà.
    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
     
    #include "serialization.h"
    #ifndef SERIALIZATION_CPP
    #define SERIALIZATION_CPP
    #define REGISTER_KEY(KEY, TYPE, TYPES...)  \
    struct KEY : public  odfaeg::Serializer<TYPE, TYPES> { \
        public : \
        KEY () {} \
        KEY (TYPE* object) : odfaeg::Serializer<TYPE, TYPES>(object) { \
        } \
    }; \
    KEY key; \
    virtual void reg() { \
        key = KEY(this); \
    } \
    template <typename Archive> \
    void load(Archive & ar) { \
        key.serialize(ar); \
        key.onLoad(); \
    } \
    template <typename Archive> \
    void save(Archive & ar) { \
        key.serialize(ar); \
        key.onSave(); \
    }
    #endif // SERIALIZATION_CPP

    Ensuite je n'ai qu'à déclarer la clé dans le fichier .h comme 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
    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
    99
    100
     
    #ifndef ODFAEG_BOUNDING_VOLUME_HPP
    #define ODFAEG_BOUNDING_VOLUME_HPP
    #include "../Math/vec4.h"
    #include <string>
    #include "../Core/serialization.cpp"
    #define BVKEY REGISTER_KEY(BOUNDINGVOLUMEKEY, odfaeg::BoundingVolume, odfaeg::BoundingBox)
    /**
      *\namespace odfaeg
      * the namespace of the Opensource Development Framework Adapted for Every Games.
      */
    namespace odfaeg {
    /**
      * \file boundingVolume.h
      * \class BoudingVolume
      * \brief Manage a bounding volume for collision detection
      * \author Duroisin.L
      * \version 1.0
      * \date 1/02/2014
      *
      * Base class of all bouding volumes of the framework used for collision detection.
      *
      */
    class BoundingVolume;
    class BoundingBox;
    class BaseInterface {
         public :
         void addChild(BoundingVolume* bv) {
            children.push_back(bv);
         }
         std::vector<BoundingVolume*> getChildren() {
            return children;
         }
         std::vector<BoundingVolume*> children;
    };
    template <typename T>
    class Interface : public BaseInterface  {
        public:        using DataType = T;
            DataType /* const */ & data() /* const*/{return t_;}
     
        protected:
            Interface(T & t) : t_(t){}
            virtual  ~Interface(){}
     
            //~Interface(){}
        private:
            T & t_;
    };
    class BoundingVolume : public BaseInterface {
    public :
     
        virtual bool intersects(BaseInterface& other) {
            //if(static_cast<C&>(other)) {
                if (children.size() == 0 && other.children.size() == 0) {
                    return intersects(other);
                }  else if (children.size() == 0 && other.children.size() != 0) {
                      for (unsigned int i = 0; i < other.children.size(); i++) {
                          if (intersects(*other.children[i]))
                                return true;
                      }
                } else {
                    for (unsigned int i = 0; i < children.size(); i++) {
                        if (other.children.size() == 0) {
                            if (children[i]->intersects(other))
                                    return true;
     
                        } else {
                            for (unsigned j = 0; j < other.children.size(); j++) {
                                 if (children[i]->intersects(*other.children[j]))
                                        return true;
                            }
                        }
                    }
                }
            //}
            return false;
        }
        virtual Vec3f getPosition() {
            return Vec3f(0, 0, 0);
        }
        virtual Vec3f getSize() {
            return Vec3f(0, 0, 0);
        }
        virtual Vec3f getCenter() {
            return Vec3f(0, 0, 0);
        }
        virtual void move (Vec3f t) {
        }
        virtual BoundingVolume* clone() {
            return new BoundingVolume(*this);
        }
        template <typename Archive>
        void serialize(Archive & ar) {
            std::cout<<"Serialize bounding volume"<<std::endl;
        }
        virtual ~BoundingVolume() {}
        BVKEY
    };
    }
    #endif // BOUNDING_AREAS

    Dans l'archive j'enregistre juste l'objet et je récupère juste la clé qui est définie dans la classe de l'objet et j'appelle la fonction sur la clé :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    template <typename O>
        void operator<<(O* object) {
             if (std::is_polymorphic<O>::value) {
                 object->reg();
                 object->save(*this);
             }

    Si je veux rajouter une classe dérivée de la classe BoundingVolume il suffit de redéfinir la clé dans le main :

    Code cpp : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    #define BVKEY REGISTER_KEY(MYBOUNDINGVOLUMEKEY, odfaeg::BoundingVolume, odfaeg::BoundingSphere)

    Maintenant je vais devoir utiliser SFINAE pour les objets polymorphiques, non polymorphique et fondamentaux. (Car il va falloir que je les distingue vu qu'ils ne s'enregistrent pas de la même façon)

    Je kiff de trop le c++11 ça te permet de faire des choses puissantes sans trop te fouler!

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