Salut, alors je vais essayé d'expliqué ma solution.
J'écris des données de tout type d'objets dans un fichier à l'aide de quelque classes et une macro qui associe un type de base à ses types dérivé en déclarant une structure qui hérite d'un sérialiseur qui contient un tuple avec un pointeur sur l'objet dérivé et un pointeur sur l'objet de base.
Cette macro ajoute donc la déclaration de cette structure dans la classe de base ainsi qu'un objet de cette structure en variable membre à la classe de base. (j'appelle cet objet, la clé)
Le sérialiseur contient plusieurs fonctions utiles :
-Une pour rechercher le type réel de l'objet et appeler la fonction de sérialisation sur le type réel de l'objet en faisant une recherche à l'aide de typeid dans le tuple.
-Une autre pour retourner l'index du pointeur sur le type réel de l'objet dans le tuple.
-Et enfin, une autre pour pouvoir allouer le pointeur sur l'objet de base avec le type réel de l'objet à la lecture. (Pour connaître le type réel de l'objet j'écris juste l'id du type réel de base de l'objet dans un fichier)
Le problème est que le compilateur n'a pas l'air d'aimer cela, bref, voici une solution que j'ai mis en place et qui marche avec l'option -fpermissive :
Voici le sérialiseur qui contient le tuple et qui contient quelque fonctions/structure utilitaire d'allocation, de recherche d'index et de type ainsi que d'appel de la fonction sur le type réel de l'objet à 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
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 #ifndef ODFAEG_SERIALIZATION #define ODFAEG_SERIALIZATION #include <iostream> #include <typeinfo> #include "tuple.h" #include <tuple> namespace odfaeg { template <int N, typename B, typename T, typename A> struct serializer { static void checkType (B* base, T tuple, A & ar) { if (std::get<N>(tuple) != nullptr && typeid(*base) == typeid(*std::get<N>(tuple))) { std::get<N>(tuple)->serialize(ar); } serializer<N-1, B, T, A>::checkType(base, tuple, ar); } }; template <typename B, typename T, typename A> struct serializer <0, B, T, A> { static void checkType (B* base, T tuple, A & ar) { if (std::get<0>(tuple) != nullptr && typeid(*base) == typeid(*std::get<0>(tuple))) { std::get<0>(tuple)->serialize(ar); } else if (base != nullptr) { base->serialize(ar); } } }; template <int N, typename B, typename T> struct sallocator { static B* instanciate (int index, T tuple) { if (N == index) { using D = typename std::remove_pointer<typename std::tuple_element<N, T>::type>::type; return new D(); } sallocator<N-1, B, T>::instanciate(index, tuple); } }; template <typename B, typename T> struct sallocator <0, B, T> { static B* instanciate (int index, T tuple) { using D = typename std::remove_pointer<typename std::tuple_element<0, T>::type>::type; return new D(); } }; template <int N, typename B, typename T> struct sindex { static int getIndex (B* base, T tuple) { if (std::get<N>(tuple) != nullptr && typeid(*base) == typeid(*std::get<N>(tuple))) { return N; } sindex<N-1, B, T>::getIndex(base, tuple); } }; template <typename B, typename T> struct sindex <0, B, T> { static int getIndex (B* base, T tuple) { using D = typename std::remove_pointer<typename std::tuple_element<0, T>::type>::type; D* derived = new D(); if (std::get<0>(tuple) != nullptr && typeid(*base) == typeid(*std::get<0>(tuple))) { return 0; } return -1; } }; template <class B, class... D> class Serializer { public : Serializer() { baseObject = nullptr; } Serializer(B* baseObject) : baseObject(baseObject) { fillTuple(derivedClasses); } void setObject(B* baseObject) { this->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 serialize(Archive & ar) { if (std::tuple_size<decltype(derivedClasses)>::value > 0) serializer<std::tuple_size<decltype(derivedClasses)>::value-1, B, decltype(derivedClasses), Archive>::checkType(baseObject, derivedClasses, ar); else baseObject->serialize(ar); } virtual void onSave() { } virtual void onLoad() { } B* sallocate(int index) { if (std::tuple_size<decltype(derivedClasses)>::value > 0) return sallocator<std::tuple_size<decltype(derivedClasses)>::value-1, B, decltype(derivedClasses)>::instanciate(index, derivedClasses); } int getIndex() { if (std::tuple_size<decltype(derivedClasses)>::value > 0) return sindex<std::tuple_size<decltype(derivedClasses)>::value-1, B, decltype(derivedClasses)>::getIndex(baseObject, derivedClasses); return -1; } private : B* baseObject; std::tuple<D*...> derivedClasses; }; } #endif // SERIALIZATION
Si le type statique de l'objet est le même que le type dynamique de l'objet, alors je retourne -1 comme index.
Voici le contenu de mon autre fichier qui n'est rien d'autre qu'une macro qui va inclure le tout dans le fichier.h de la classe de base pour que je puisse avoir accès à la clé dans l'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 #include "serialization.h" #ifndef SERIALIZATION_CPP #define SERIALIZATION_CPP #define REGISTER_KEY(KEY, TYPE, TYPES...) \ struct KEY : public odfaeg::Serializer<TYPE, TYPES> { \ public : \ KEY () {} \ void register_object (TYPE* object) { \ setObject(object); \ } \ TYPE* allocate_object(int index) { \ return sallocate(index); \ } \ int getTypeIndex () { \ return odfaeg::Serializer<TYPE, TYPES>::getIndex(); \ } \ template <typename Archive> \ void serialize_object (Archive & ar, int) { \ odfaeg::Serializer<TYPE, TYPES>::serialize(ar); \ } \ }; \ KEY key; \ static TYPE* allocate (int index) { \ static KEY aKey; \ return aKey.allocate_object(index); \ } #endif // SERIALIZATION_CPP
Et enfin dans l'archive je n'ai plus qu'à accéder à la clé, afin d'écrire tout ça :
Si l'objet possède un pointeur de fonction prenant une archive et un int, alors, cela veut dire que il est possible que l'objet soit polymoprhique, sinon, j'appelle directement la fonction serialize contenue dans la classe de l'objet. (J'ai deux fonction, une contenue dans la classe de l'objet et une autre contenue dans la définition de la clé), le int dans la fonction elle même ne sert donc à rien, il me sert juste à appliquer le principe de SFINAE.
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 template <class O, class... D, class = typename std::enable_if<std::is_member_function_pointer<void(O::*)(OTextArchive&)>::value>::type, class = typename std::enable_if<std::is_member_object_pointer<decltype(&O::key)>::value>::type, class = typename std::enable_if<!std::is_same<O, std::string>::value>::type, class = typename std::enable_if<!sizeof...(D)>::type> void operator() (O* object, D...) { std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(reinterpret_cast<unsigned long long int>(object)); if (it != adresses.end()) { buffer<<it->second<<" "; } else { int index = -1; std::pair<unsigned long long int, unsigned long long int> newAddress (reinterpret_cast<unsigned long long int>(object), nbSerialized); adresses.insert(newAddress); if (typeid(decltype(*object)) == typeid(*object)) { std::cout<<"non polymoprhic version"<<std::endl; buffer<<newAddress.second<<" "; buffer<<index<<" "; object->serialize(*this); } else { std::cout<<"polymoprhic version"<<std::endl; object->key.register_object(object); index = object->key.getTypeIndex(); buffer<<newAddress.second<<" "; buffer<<index<<" "; object->key.serialize_object(*this, 0); } nbSerialized++; } }
Je sauve donc l'index du type dynamique de l'objet. (-1 si le type dynamique est le même que le type statique)
Cette solution marche extrêmement bien et me permet de définir ce que je veux sérialiser dans le fichier.h de la classe de base et même redéfinir ça dans le main si le fichier.h est contenu dans une librairie pour ne pas avoir à modifer le .h de la librairie si je rajoute des classes dérivées.
Cependant il y a un petit problème à la lecture :
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 void operator() (O** object, D...) { unsigned long long int id; int index; buffer>>id; buffer>>index; std::map<unsigned long long int, unsigned long long int>::iterator it = adresses.find(id); if (it != adresses.end()) { *object = reinterpret_cast<O*>(it->second); } else { if (index == -1) { std::cout<<"non polymorphic version"<<std::endl; *object = new O(); (*object)->serialize(*this); std::pair<unsigned long long int, unsigned long long int> newAddress (id, reinterpret_cast<unsigned long long int>(object)); adresses.insert(newAddress); } else { std::cout<<"polymorphic version"<<std::endl; *object = O::allocate(index); (*object)->key.register_object(*object); (*object)->key.serialize_object(*this, 0); std::pair<unsigned long long int, unsigned long long int> newAddress (id, reinterpret_cast<unsigned long long int>(object)); adresses.insert(newAddress); } nbDeserialized++; } }
Que voici :
J'ai donc rajouté l'option -fpermissive pour que gcc me foute la paix car de toute façon il n'ira jamais dans le else si le type dynamique est diffférent du type statiqque, mais je ne sais pas si c'est la meilleur solution.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 ||=== Build: Debug in ODFAEG-DEMO (compiler: GNU GCC Compiler) ===| /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|9|warning: "G2DEKEY" redefined [enabled by default]| /usr/local/include/odfaeg/Graphics/2D/entity.h|13|note: this is the location of the previous definition| /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|36|warning: "/*" within comment [-Wcomment]| /usr/local/include/odfaeg/Core/archive.h||In instantiation of void odfaeg::ITextArchive::operator()(O**, D ...) [with O = odfaeg::BoundingBox; D = {}; <template-parameter-1-3> = void; <template-parameter-1-4> = void; <template-parameter-1-5> = void; <template-parameter-1-6> = void]:| /home/laurent/Développement/Projets-c++/ODFAEG-DEMO/main.cpp|65|required from here| /usr/local/include/odfaeg/Core/archive.h|513|error: invalid conversion from odfaeg::BoundingVolume* to odfaeg::BoundingBox* [-fpermissive]| ||=== Build failed: 1 error(s), 4 warning(s) (0 minute(s), 5 second(s)) ===|
Partager