Ecrire structure dans un fichier + héritage

Ecrire structure dans un fichier + héritage - C++ - Programmation

Marsh Posté le 24-06-2003 à 20:17:18    

Je veux écrire le contenu de plusieurs structures dans un fichier. J'ai le code suivant qui définit une structure B dérivant de A avec un peu de polymorphisme:
 

Code :
  1. struct A {
  2.     unsigned long m_ID;
  3.    
  4.     virtual int byteRead() = 0;
  5. }
  6. struct B : public A {
  7.     unsigned long m_Index1;
  8.     unsigned long m_Type;
  9.     int byteRead() {..}
  10. }


 
quand je fais un bête

Code :
  1. ofstream file(....);
  2. B objB;
  3. file.write(reinterpret_cast<const char*>(&objB), sizeof(objB));


 
ca ne devrait bien sur pas marcher parce que sizeof(objB) contient AUSSI d'autres info (type la taille du pointer sur la fontion byteRead?)...Y a t-il une solution, sachant que j'ai absolument besoin du polymorphisme pour connaître le type exact de ma variable une fois downcasté!
 
Sais pas si c'est clair....


Message édité par Willyzekid le 24-06-2003 à 20:22:44

---------------
Horizon pas Net, reste à la buvette!!
Reply

Marsh Posté le 24-06-2003 à 20:17:18   

Reply

Marsh Posté le 24-06-2003 à 20:24:55    

Fait toi une methode virtuelle pure 'write' dans tes objets.
 

Code :
  1. struct A {
  2.    unsigned long m_ID;
  3.  
  4.    virtual int byteRead() = 0;
  5.    virtual void write(ofstream os)
  6.   {
  7.      os << m_ID;
  8.   }
  9. }
  10. struct B : public A {
  11.    unsigned long m_Index1;
  12.    unsigned long m_Type;
  13.    int byteRead() {..}
  14.    virtual void write(ofstream os)
  15.   {
  16.      A::write(os);
  17.      os << m_Index1 << m_Type;
  18.   }
  19. }

Reply

Marsh Posté le 24-06-2003 à 20:35:12    

Joel F a écrit :

Fait toi une methode virtuelle pure 'write' dans tes objets.


 
Ouais, c'est ce que j'ai fait mais le truc c'est qu'après plusieurs dérivation, j'arrive a avoir une bonne dizaine (voire plus) de champs dans mes classes.
Du coup, j'aimerais les écrire tous en un seul bloc, avec un seul appel à write() pour optimiser les perf. Ca veut dire connaitre le pointeur sur le début des données en mémoire, et la taille de celles-ci.
 
Idem pour read...
 
C'est d'autant plus important que je peux parcourir un fichier de plusieurs millions de records comme ca pour les extraires l'un après l'autre et lire la valeur d'un des champs (ie. utilisation database).
 
Merci quand même!


---------------
Horizon pas Net, reste à la buvette!!
Reply

Marsh Posté le 24-06-2003 à 21:20:12    

Un truc utilisé dans Half-Life et les MFC (et un peu partout je crois) : mettre une table statique par niveau de dérivation pour définir toutes les variables exportables d'une classe. eg :
 
class a
{
  int varA;
  int varB;
 
};
 
defineTableVars(a)
  defineVar("int", offsetof(class a, varA))
  defineVar("int", offsetof(class a, varB))
defineTableVars(a)
 
class b : public a
{
  char c;
 
  defineTable(b, a)
};
 
defineTableVars(b)
  defineVar("char", offsetof(class b, varC))
defineTableVars(b)
 
Une fois que tu as ça, tu peux constituer pour chaque classe un tableau statique de tout ce qu'elle peut contenir, importer / exporter automatiquement, scripter, etc. Tu n'as plus qu'à parser ce tableau d'offsets pour sérialiser toutes les données de la classe dans un bloc.  
 
Ce genre de système te permet également le versioning : si tu ne fais pas ça comme un gros goret (eg tu checkes le nom de la variable à chaque écriture), tu peux rajouter / enlever des variables de tes classes à loisir, sans péter les formats des fichiers existants.

Reply

Marsh Posté le 24-06-2003 à 22:33:03    

youdontcare a écrit :

Un truc utilisé dans Half-Life et les MFC (et un peu partout je crois) : mettre une table statique par niveau de dérivation pour définir toutes les variables exportables d'une classe. eg :
(...)
Ce genre de système te permet également le versioning : si tu ne fais pas ça comme un gros goret (eg tu checkes le nom de la variable à chaque écriture), tu peux rajouter / enlever des variables de tes classes à loisir, sans péter les formats des fichiers existants.


 
Tiens c'est la première fois que je vois ca...Faut dire que je me plonge pas dans du MFC à longueur de journée! :o
 
Sinon, je suis pas sûr que ca règle mon problème s'il faut parser tous les offsets. Ca revient a faire un appel a ofstream::write() pour chaque variable.
 
Cela dit, je dois t'avouer que comme je comprends pas ce que fait defineTableVars(a), etc... C'est une macro? tu l'as sous la main?


---------------
Horizon pas Net, reste à la buvette!!
Reply

Marsh Posté le 24-06-2003 à 22:45:01    

>> Sinon, je suis pas sûr que ca règle mon problème s'il faut parser tous les offsets. Ca revient a faire un appel a ofstream::write() pour chaque variable.
 
Ça dépend. Une optim de ce code est de dumper tout l'objet sans la vtable ( ((char*)this)+4, sizeof(*this)-4) et d'écrire tout d'un coup. Tu sauvegardes une seule description des données par classe. Au chargement, tu peux relire ça similairement si la structure enregistrée correspond à la structure runtime.
 
Mais faut pas te leurrer, soit tu fais un truc hypra simple (que des structures) qui explosera lamentablement au moindre changement d'une structure, soit tu fais un truc clean. (sauvegarde des noms des variables, test, etc.) Et avant de dire que c'est lent, tu benches :D
 
Je ne sais pas comment bosse ofstream, s'il écrit sur le dur à chaque appel, c'est mal. Un bon buffer mémoire remédie à ça.
 
>> Cela dit, je dois t'avouer que comme je comprends pas ce que fait defineTableVars(a), etc... C'est une macro? tu l'as sous la main?
 
Oui. C'est pour t'encourager à chercher par toi-même, car je n'ai pas ça sous la main. Comme je l'ai dit, un tableau statique par niveau de dérivation.  
 
* Une macro dans la définition de la classe pour ... définir la table (defineTable)
* Une macro pour implémenter la table (defineTableVars)
* Une macro pour définir chaque variable (defineVar)

Reply

Marsh Posté le 24-06-2003 à 22:49:39    

[:violon] suis je le seul a trouver ca moche ???

Reply

Marsh Posté le 25-06-2003 à 09:00:47    

Joel F a écrit :

[:violon] suis je le seul a trouver ca moche ???


 [:plusun]  
 
C hyper crade, hyper pas portable.


---------------
Le Tyran
Reply

Marsh Posté le 25-06-2003 à 09:17:51    

Ce qu'on faisait dans ma precedente boite
c'est que pour chaque classe on creait une instance
vide de la classe, tout simplement pour pouvoir copier le pointeur vers la vtable et pouvoir identifier les classes que l'on allait charger (identifiées par des metaobjets representant les classes).  
 
Bien evidemment sur console il n'y avait pas forcément de fixup des pointeurs si l'on se contentait d'un chargement par overlay.
Sur PC il etait necessaire de fixuper les pointeurs par contre.
 
LeGreg

Reply

Marsh Posté le 25-06-2003 à 09:41:45    

legreg a écrit :

Ce qu'on faisait dans ma precedente boite
c'est que pour chaque classe on creait une instance
vide de la classe, tout simplement pour pouvoir copier le pointeur vers la vtable et pouvoir identifier les classes que l'on allait charger (identifiées par des metaobjets representant les classes).  
 
Bien evidemment sur console il n'y avait pas forcément de fixup des pointeurs si l'on se contentait d'un chargement par overlay.
Sur PC il etait necessaire de fixuper les pointeurs par contre.
 
LeGreg


 
Argh c encore pire  :cry:


---------------
Le Tyran
Reply

Marsh Posté le 25-06-2003 à 09:41:45   

Reply

Marsh Posté le 25-06-2003 à 09:56:37    

Des fois je me demande pourquoi y a des gens compétents qui se sont fait iech à inventer l'héritage tout ca ...
 
M'enfin, ....

Reply

Marsh Posté le 25-06-2003 à 09:58:39    

Attend j'en ai une encore pire: tu mape ton fichier en mémoire et tu fai un new de palcement avec el constructeur de recopie  
 
 :pt1cable:


---------------
Le Tyran
Reply

Marsh Posté le 25-06-2003 à 10:00:24    

LetoII a écrit :

Attend j'en ai une encore pire: tu mape ton fichier en mémoire et tu fai un new de palcement avec el constructeur de recopie  
 
 :pt1cable:  


 
 :lol:  :lol:  :pt1cable:  
Arrete on dirait du code de kitusais !!

Reply

Marsh Posté le 25-06-2003 à 10:13:28    

Joel F a écrit :


 
 :lol:  :lol:  :pt1cable:  
Arrete on dirait du code de kitusais !!


 
je vois pas dekituparle  :??:


---------------
Le Tyran
Reply

Marsh Posté le 25-06-2003 à 15:09:58    

Bon les enfants, vous auriez pas un truc portable. Non parce que j'ai aussi pensé à jouer avec la vtable mais là, c'est non seulement ca dépend de l'os mais ca commence aussi à dépendre du compilo.
 
Et moi, comble du programmeur, j'ai besoin d'un truc portable :)
 
J'en reviens effectivement pas qu'il n'y ait rien de prévu dans les spécifications... :(


---------------
Horizon pas Net, reste à la buvette!!
Reply

Marsh Posté le 25-06-2003 à 15:14:43    

Utilise un stringstream (je sais plus le nom exact de la classe)
 
Tu écrit tout t champ dedans et après tu écrit ta chaine dans un fichier.
Si tu veux faire du binaire tu écrit tout t champ dans un buffer (pense à fixer l'indianess) et écrit le buffer dans le fichier (encore que du binaire avec un ftream faut envouloir)


---------------
Le Tyran
Reply

Marsh Posté le 25-06-2003 à 15:54:20    

LetoII a écrit :

Utilise un stringstream (je sais plus le nom exact de la classe)
 
Tu écrit tout t champ dedans et après tu écrit ta chaine dans un fichier.
Si tu veux faire du binaire tu écrit tout t champ dans un buffer (pense à fixer l'indianess) et écrit le buffer dans le fichier (encore que du binaire avec un ftream faut envouloir)


 
Effectivement c'est du binaire que je veux, et non j'utilise pas la STL pour ça (suis pas maso). Je fais "une sorte de base de données" donc mes fonctions d'entrée sortie sont trés low level (à la limites des appels systèmes).
 
J'ai trouvé la solution élégante (dans mon cas)! En fait, je viens de refaire le design de plusieurs classes pour pouvoir utiliser des templates pour mes structures...sans dérivation. Ca me force a revoir la plupart du design sous un jour nouveau mais en même temps, ca règle pas mal de problème.
 
Exit l'héritage et le polymorphisme sur les struct A, B etc.
 
Merci en tout cas!


Message édité par Willyzekid le 25-06-2003 à 15:56:41

---------------
Horizon pas Net, reste à la buvette!!
Reply

Marsh Posté le 25-06-2003 à 17:15:04    

à la limites des appels systèmes
!=
portable (dans la plupart des cas)
 
il me semble en effet que plus on pousse l'optimisation, moins le code est portable, non ?


---------------
-( BlackGoddess )-
Reply

Marsh Posté le 25-06-2003 à 18:20:16    

la methode que j'ai cité etait
"relativement" portable.
 
En gros on avait 5 plateformes de destination:
Gamecube, Playstation 2, Xbox, Pc Windows, PC Linux.
 
Par un trick au chargement et a la sauvegarde
nos binaires etaient compatibles entre les differentes plateformes. (meme si on pouvait laisser tomber la compatibilité pour une plus grande performance)
 
Je ne fais pas une generalité mais sur console, typiquement, les chargements sont mieux s'ils sont reduits au minimum.
De plus si tu es limité a la taille de l'executable que tu peux charger en memoire tu utilises l'overlay (recouvrement d'une partie de la memoire en fonction des besoin).
Par ailleurs, contrairement au PC, d'un chargement a l'autre tu peux sauvegarder tes pointeurs, ou les fixuper en dur, tu es assuré qu'au prochain chargement, si tu fais bien les choses, les données de niveau que tu charges seront au meme emplacement mémoire (parfois tu es obligé quand tu as une rom plutot qu'une ram).
 
A+
LeGreg

Reply

Marsh Posté le 25-06-2003 à 18:36:33    

BlackGoddess a écrit :

à la limites des appels systèmes
!=
portable (dans la plupart des cas)
 
il me semble en effet que plus on pousse l'optimisation, moins le code est portable, non ?


 
Oui mais je limite ces appels à un module/librairie "io" bien définie pour éviter de répercuter du code propriétaire à d'autres modules. D'autre part ce code reste un minimum portable gràce à des #define WIN32, etc. bien sentis
 
Les autres modules qui utilisent les fonctions de "io" n'ont pas à savoir comment c'est géré, et à utiliser du code propriétaire.


---------------
Horizon pas Net, reste à la buvette!!
Reply

Marsh Posté le 25-06-2003 à 19:00:36    

Citation :

ca ne devrait bien sur pas marcher parce que sizeof(objB) contient AUSSI d'autres info (type la taille du pointer sur la fontion byteRead?)...


 
Au chargement tu remplaces ton pointeur vers ta vtable
contre un offset vers un metachamp dans ton fichier qui va decrire ton type de données et en particulier le nom par exemple ou un hash quelconque qui ne change que si tu changes la version du fichier.
Ce metachamp va etre comparé au chargement au métachamp deja present dans les objets "vides" créés préalablement.
De cet objet vide tu vas récupérer le "vrai"pointeur vers la vtable.
 
Evidemment ca impose de connaitre un peu ton compilateur et de faire des #ifdef puisque malheureusement tous les compilateurs ne stockent pas leur pointeur de vtable au meme offset (certains utilisent un offset négatif !).
 
LeGreg

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed