#include "defs.h" #include #include "peidainit.h" #include "ppersist.h" #include "dates.h" #ifndef RFIO #include #endif #ifdef __mac__ #include "unixmac.h" #include #endif #if SWAP==1 #define IS_BIG_ENDIAN 0 #else #define IS_BIG_ENDIAN 1 #endif //++ // Class PPersistMgr // Lib Outils++ // include ppersist.h // // Classe gestionnaire d'objets persistants. Les méthodes statiques // de cette classe permettent d'enregistrer des classes persistantes, // et de relire automatiquement des objets. //-- //++ // Links Voir // PPersist // PShPersist //-- PPersistMgr::ClassList* PPersistMgr::classList = NULL; PShPersist::ObjList* PShPersist::objList = NULL; //++ void PPersistMgr::RegisterClass(int_4 classId, ClassCreatorFunc f, bool shared) // // Enregistre une nouvelle classe auprès du gestionnaire. classId doit // identifier de façon unique la classe. f doit créer un nouvel objet // de cette classe (qui doit hériter de PPersist), non initialisé, et qui // sera relu à partir du fichier d'objets persistants. // // Si shared est true, la classe doit hériter de PShPersist, et la persistance // gère le fait que plusieurs objets Xi puissent faire référence à un même objet A. // A, qui hérite donc de PShPersist, ne sera écrit qu'une seule fois dans le fichier // lorsque les Xi sont écrits, et la référence à A sera mise à jour lors de la lecture // des Xi... //-- { DBASSERT(classList); if (classList->size() && (classList->find(classId) != classList->end())) { cerr << "RegisterClass : Error, " << hex << classId << dec << " already registered." << endl; THROW(dupIdErr); } ClassListEntry entry = {shared, f}; (*classList)[classId] = entry; } PPersistMgr::ClassCreatorFunc PPersistMgr::FindCreatorFunc(int_4 classId) { DBASSERT(classList); return (*classList)[classId].f; } PPersistMgr::ClassCreatorFunc PPersistMgr::FindCreatorFunc(int_4 classId, bool& isShared) { DBASSERT(classList); ClassListEntry entry = (*classList)[classId]; isShared = entry.shared; return entry.f; } //++ PPersist* PPersistMgr::ReadObject(PInPersist& s) // // Lit un objet dans le stream s, en créant automatiquement un // objet du bon type. //-- { DBASSERT(classList); DBASSERT(PShPersist::objList); // On commence par lire le type de l'objet int_4 classId; s >> classId; // On recupere le createur, et on regarde si l'objet est partage bool shared; if (!classList->size() || (classList->find(classId) == classList->end())) { cerr << "PPersistMgr::ReadObject Error : Class " << classId << " not registered" << endl; THROW(notFoundErr); } PPersistMgr::ClassCreatorFunc f = FindCreatorFunc(classId, shared); if (!f) THROW(notFoundErr); PPersist* object; if (shared) { // Si l'objet est partage, ce qui suit est son identificateur, sur 4 octets // et un octet ensuite qui dit si l'objet est present. int_4 objectId; char objectSaved; s >> objectId >> objectSaved; #if 0 // On regarde si l'objet est deja en memoire object = PShPersist::FindObject(objectId); // On verifie qu'on est dans une situation coherente... if ((!objectSaved) == (!object)) THROW(inconsistentErr); #else if (!objectSaved) { object = PShPersist::FindObject(objectId); if (!object) THROW(inconsistentErr); } #endif if (objectSaved) { object = f(); object->ReadSelf(s); ((PShPersist*)object)->mObjectID = objectId; (*PShPersist::objList)[objectId] = (PShPersist*)object; } } else { object = f(); object->ReadSelf(s); } return object; } //++ // Class PPersist // Lib Outils++ // include ppersist.h // // Classe de base pour des objets persistants. Pour créer un objet // persistant : // - Hériter de PPersist. // - Définir un numéro d'identification de la classe, unique dans Peida // - Implémenter "ClassId()" // - Implémenter "WriteSelf" et "ReadSelf", qui doivent écrire toutes les variables // membres que l'on souhaite écrire, et les relire dans le même ordre. // Pour écrire une référence à un objet : l'objet doit être un PPersist, // et il suffit d'appeler "Write" sur cet objet, et "PPersistMgr::ReadObject". // Si plusieurs objets font référence au même, pour éviter de l'écrire plusieurs // fois, il faut que cet objet soit un PShPersist. // - Pour que le fichier soit portable, écrire et lire les variables membres en utilisant // les fonctions PutXX/GetXX de PInPersist/POutPersist. // // Attention: les méthodes à redéfinir sont WriteSelf et ReadSelf, mais il ne faut jamais // les appeler directement. Seuls Write et Read peuvent être appelées par l'utilisateur. //-- //++ // Links Voir // PInPersist // POutPersist // PPersistMgr // PShPersist //-- PShPersist* PShPersist::FindObject(int_4 objId) { DBASSERT(objList); return (*objList)[objId]; } //++ void PPersist::Write(string const& fn) const // // Ecrit l'objet dans un nouveau fichier ppersist "fn". //-- { POutPersist of(fn); Write(of); } //++ void PPersist::Read(string const& fn) // // Relit l'objet dans le fichier ppersist "fn". Il faut connaître a priori // le type de l'objet. Pour une relecture avec création automatique du bon // objet, utiliser PPersistMgr::ReadObject. //-- { PInPersist inf(fn); Read(inf); } //++ void PPersist::Write(POutPersist& s) const // // Ecrit l'objet dans le fichier PPersist. //-- { // On doit tout d'abord ecrire notre type s << ClassId(); // Puis on s'ecrit, tout betement... WriteSelf(s); } //++ void PPersist::Read(PInPersist& s) // // Relit l'objet dans le fichier ppersist. Il faut connaître a priori // le type de l'objet. Pour une relecture avec création automatique du bon // objet, utiliser PPersistMgr::ReadObject. //-- { // A n'utiliser que si on connait a priori // le vrai type de l'objet. // On doit tout d'abord lire notre type int_4 classId; s >> classId; if (classId != ClassId()) { cerr << "PPersist::Read() Object type (=" << ClassId() << ") mismatch (type in file=" << classId << ")" << endl; THROW(typeMismatchErr); } // Puis on se lit, tout betement... ReadSelf(s); } //++ int_4 PPersist::Write(POutPersist& s, int_4 key) const // // Ecrit l'objet dans le fichier PPersist avec un tag ayant "key" comme // valeur du champ clé du tag. //-- { int_4 rc; // On ecrit le tag de positionnement et on recupere le numero de tag rc = s.WriteTag(key, NULL); // avant d'ecrire l'objet lui-meme Write(s); return(rc); } //++ int_4 PPersist::Write(POutPersist& s, int_4 key, string& nom) const // // Ecrit l'objet dans le fichier PPersist avec un tag ayant "key", nom comme // valeurs des champs clé et nom du tag. //-- { int_4 rc; // On ecrit le tag de positionnement et on recupere le numero de tag rc = s.WriteTag(key, nom.c_str()); // avant d'ecrire l'objet lui-meme Write(s); return(rc); } //++ void PPersist::ReadAtTag(PInPersist& s, int_4 tagid) // // Lit l'objet à la position du tag numéro "tagid". //-- { if (s.GotoTag(tagid)) Read(s); else { cerr << "PInPersist::GotoTag() Error ! \n" << "From PPersist::ReadAtTag(PInPersist, tag= " << tagid << ") " << endl; THROW(fileErr); } return; } //++ void PPersist::ReadAtKey(PInPersist& s, int_4 key) // // Lit l'objet à la position du tag contenant la clé "key". //-- { if (s.GotoKey(key)) Read(s); else { cerr << "PInPersist::GotoKey() Error ! \n" << "From PPersist::ReadAtKey(PInPersist, key= " << key << ") " << endl; THROW(fileErr); } return; } //++ // virtual void PPersist::ReadSelf(PInPersist&)=0 // Méthode virtuelle pure à redéfinir. Elle est appelée par Read // et PPersistMgr::ReadObject. Il faut relire les variables membres, // dans l'ordre où elles ont été écrites par WriteSelf. // virtual void PPersist::WriteSelf(POutPersist&) const=0 // Méthode virtuelle pure à redéfinir. Elle est appelée par Write. // Il faut écrire les variables membres, // dans l'ordre où elles seront relues par ReadSelf. //-- //++ // Class PShPersist // Lib Outils++ // include ppersist.h // // Classe de base pour des objets persistants partagés. // Un tel objet ne sera écrit qu'une seule fois dans un fichier // donné. Les tentatives suivantes d'écriture n'écrivent qu'une // référence à la première écriture. // // Lors de la lecture, l'objet est créé lors de la première lecture, // puis l'adresse de cet objet est retournée lors des lectures suivantes. //-- //++ // Links Parents // PPersist //-- //++ void PShPersist::Write(POutPersist& s) const // // Ecrit l'objet dans le fichier s. // // En fait, l'objet est physiquement écrit une seule fois. //-- { DBASSERT(objList); // On doit tout d'abord ecrire notre type s << ClassId(); // On doit ensuite ecrire notre identificateur et le flag d'ecriture char write = (mObjectID == 0) ? 255 : 0; if (mObjectID == 0) { (int_4&)mObjectID = (*objList).size() ? (*((*objList).rbegin())).first+1 : 1; (*objList)[mObjectID] = (PShPersist*)this; } s << mObjectID << write ; // Puis, si necessaire, on s'ecrit if (write) WriteSelf(s); } PShPersist::~PShPersist() // Une securite peut-etre facultative { DBASSERT(objList); if (mObjectID) { DBASSERT(!objList->size() && (objList->find(mObjectID) != objList->end())); objList->erase(mObjectID); } } void PPersistMgr::Reset() { DBASSERT(PShPersist::objList); for (PShPersist::ObjList::iterator i = PShPersist::objList->begin(); i != PShPersist::objList->end(); i++) (*i).second->mObjectID = 0; // Une securite peut-etre facultative PShPersist::objList->erase(PShPersist::objList->begin(), PShPersist::objList->end()); } #define PIOP_Delim '\n' // Delimiteur de ligne // Les noms ne peuvent depasser 255 caracteres #define MAXTAGNAMELEN 255 //++ // Class PInPersist // Lib Outils++ // include ppersist.h // // Fichier d'objets persistants, en lecture. //-- //++ PInPersist::PInPersist(string const& flnm, bool scan) // // Constructeur. Ouvre le fichier. //-- { mSbsz = 0; // Pour conserver les noms des tags ( V4 et au dela ) mSbuf = NULL; mTags = NULL; mNTags = 0; // " " " " fName = flnm; #ifdef RFIO s = new erosifstream(flnm.c_str(),"rb"); #else // $CHECK$ EA ios::binary (ou ios::bin) doit exister PARTOUT. // Voir note dans defs.h pres de HAS_IOS_BIN #if defined(__MWERKS__) s = new ifstream(flnm.c_str(),ios::in | ios::binary); // ios::binary pas connnu partout - $CHECK$ Reza 13/02/98 #else s = new ifstream(flnm.c_str(),ios::in ); #endif #endif PPersistMgr::Reset(); char rbuf[36]; GetBytes(rbuf, 32); if (strncmp(rbuf,"PEIDA-PPersistFile", 18) != 0) { cerr << "PInPersist::PInPersist Error : File " << flnm << " (empty file or bad header)" << endl; THROW(fileErr); } version = atoi(rbuf+20); bigEndian = IS_BIG_ENDIAN; // Les V2 sont relus en mode natif if (version >= 3) { GetBytes(rbuf, 32); if (strncmp(rbuf,"BIG-ENDIAN",10) == 0) bigEndian = true; else if (strncmp(rbuf,"LITTLE-ENDIAN",13) == 0) bigEndian = false; else { cerr << "PInPersist::PInPersist Error : File" << flnm; cerr << " (V>=3 et ni BIG-ENDIAN, ni LITTLE-ENDIAN" << endl; THROW(fileErr); } } if (version >= 5) { // On lit la date de creation (V5 et au-dela) GetBytes(rbuf, 32); rbuf[32] = '\0'; for(int k=0; k<32; k++) if (rbuf[k] == ' ') { rbuf[k] = '\0'; break; } creationdate = rbuf; } // Fin de lecture de date else creationdate = "??/??/??" ; // Le fichier peut contenir des tag (V4 et au dela) if ( (version >= 4) && scan ) Scan(); } PInPersist::~PInPersist() { list::iterator it; for(it = mSbuffs.begin(); it != mSbuffs.end(); it++) delete[] (*it); if (mTags) delete[] mTags; delete s; PPersistMgr::Reset(); } // A cause de aCC qui ne veut pas char* b = "" static char ChaineVide[2] = {'\0', '\0'}; void PInPersist::Scan() { // On parcourt le fichier depuis la fin pour identifier tous les tags int_8 debut, prev, premier; int_4 ntag, ntc, ll; uint_2 ln; char pad[4]; #if defined(STREAMPOS_IS_CLASS) && !defined(RFIO) debut = s->tellg().offset(); #else debut = s->tellg(); #endif premier = 0; s->seekg(-(sizeof(int_8)+sizeof(int_4)), ios::end); GetI8(prev); GetI4(ntag); if ( (ntag <= 0) || (prev < 0) ) { s->seekg(debut,ios::beg); return; } mNTags = ntag; mTags = new PPFTag[ntag]; PPFTag* tag; while( (prev > 0) && (ntag > 0) ) { s->seekg(prev); if (ntag == 1) premier = prev; GetI8(prev); GetI4(ntc); ntag--; if (ntc != ntag) { cerr << "PInPersist::Scan() / Error: NTag,TC= " << ntag << "," << ntc << endl; THROW(fileErr); } tag = mTags+ntag; GetI4(tag->key); GetU2(ln); if (ln > 0) { tag->lnom = ln; tag->nom = GetCStr(ln); GetBytes(tag->nom, ln+1); ll = 3-((ln+2)%4); GetBytes(pad, ll); } else { GetU2(ln); tag->lnom = 0; tag->nom = ChaineVide; } // On recupere la position pour l'objet qui suit #ifdef STREAMPOS_IS_CLASS tag->popos = s->tellg().offset(); #else tag->popos = s->tellg(); #endif GetI4(tag->cid); } if ( (ntag != 0) || (prev >= 0) ) { // En principe , ca ne doit pas arriver cerr << "PInPersist::Scan() / Error: (End: ntag=" << ntag << " prev=" << (int_4)prev << endl; THROW(fileErr); } // on se repositionne au debut du 1er objet du fichier if (premier == debut) GotoTag(0); else s->seekg(debut); return; } char* PInPersist::GetCStr(uint_2 sz) { char* rs; sz += 1; // Pour le 0 de la fin if (sz > mSbsz) { mSbsz = (sz > (MAXTAGNAMELEN+1)) ? sz : (MAXTAGNAMELEN+1); mSbuf = new char[mSbsz]; mSbuffs.push_back(mSbuf); } rs = mSbuf; mSbuf += sz; mSbsz -= sz; return(rs); } bool PInPersist::GotoTag(int_4 num) { if ( (num < 0) || (num >= mNTags) ) return(false); s->seekg(mTags[num].popos); return(true); } bool PInPersist::GotoKey(int_4 key, int rang) { if (rang < 0) return(false); int n = 0, i; int fnd = -1; for(i=0; i= 0) return(GotoTag(fnd)); else return(false); } int PInPersist::NbKey(int_4 key) { int n = 0, i; for(i=0; i= mNTags) ) return(0); cid = mTags[num].cid; ln = mTags[num].lnom; return(mTags[num].key); } string PInPersist::TagName(int_4 num) { if ( (num < 0) || (num >= mNTags) ) return(""); return((string)mTags[num].nom); } //++ // void PInPersist::GetByte(char& c) // void PInPersist::GetBytes(void* ptr, size_t bytes) // void PInPersist::GetR4 (r_4& result) // void PInPersist::GetR4s (r_4* tab, size_t n) // void PInPersist::GetR8 (r_8& result) // void PInPersist::GetR8s (r_8* tab, size_t n) // void PInPersist::GetI2 (int_2& result) // void PInPersist::GetI2s (int_2* tab, size_t n) // void PInPersist::GetU2 (uint_2& result) // void PInPersist::GetU2s (uint_2* tab, size_t n) // void PInPersist::GetI4 (int_4& result) // void PInPersist::GetI4s (int_4* tab, size_t n) // void PInPersist::GetU4 (uint_4& result) // void PInPersist::GetU4s (uint_4* tab, size_t n) // void PInPersist::GetI8 (int_8& result) // void PInPersist::GetI8s (int_8* tab, size_t n) // void PInPersist::GetU8 (uint_8& result) // void PInPersist::GetU8s (uint_8* tab, size_t n) // Lecture de données portables depuis le fichier PPersist. Pour chaque type // de données, on peut lire une valeur, ou un tableau de valeurs. // void PInPersist::GetLine(char* ptr, size_t len) // Lecture d'une ligne de texte depuis le fichier PPersist. //-- void PInPersist::GetByte(char& c) { GetBytes(&c, 1); } void PInPersist::GetBytes(void* ptr, size_t bytes) { s->read((char*)ptr, bytes); } static inline void bswap8(void* p) { uint_8 tmp = *(uint_8*)p; *(uint_8*)p = ((tmp >> (7*8)) & 0x000000FF) | ((tmp >> (5*8)) & 0x0000FF00) | ((tmp >> (3*8)) & 0x00FF0000) | ((tmp >> (1*8)) & 0xFF000000) | ((tmp & 0xFF000000) << (1*8)) | ((tmp & 0x00FF0000) << (3*8)) | ((tmp & 0x0000FF00) << (5*8)) | ((tmp & 0x000000FF) << (7*8)); } static inline void bswap4(void* p) { uint_4 tmp = *(uint_4*)p; *(uint_4*)p = ((tmp >> 24) & 0x000000FF) | ((tmp >> 8) & 0x0000FF00) | ((tmp & 0x0000FF00) << 8) | ((tmp & 0x000000FF) << 24); } static inline void bswap2(void* p) { uint_2 tmp = *(uint_2*)p; *(uint_2*)p = ((tmp >> 8) & 0x00FF) | ((tmp & 0x00FF) << 8); } void PInPersist::GetR4 (r_4& result) { GetBytes(&result, sizeof(r_4)); if (bigEndian != IS_BIG_ENDIAN) bswap4(&result); } void PInPersist::GetR4s (r_4* tab, size_t n) { GetBytes(tab, n*sizeof(r_4)); if (bigEndian == IS_BIG_ENDIAN) return; for (unsigned int i=0; igetline(ptr, len, PIOP_Delim); } //++ // Class POutPersist // Lib Outils++ // include ppersist.h // // Fichier d'objets persistants, en écriture. //-- //++ // POutPersist(string const& flnm, int endianness = PPersist::PPS_NATIVE) // // Crée un nouveau fichier ppersist. Par défaut, il est petit=boutien // sur machines petit-boutiennes, et gros-boutien sur machines // gros-boutiennes. On peut explicitement spécifier PPersist::PPS_LITTLE_ENDIAN // ou PPersist::PPS_BIG_ENDIAN. //-- POutPersist::POutPersist(string const& flnm, int endianness) { if (endianness == -1) bigEndian = IS_BIG_ENDIAN; else bigEndian = endianness; #ifdef RFIO s = new erosofstream(flnm.c_str(),"wb"); #else #if defined(__MWERKS__) s = new ofstream(flnm.c_str(),ios::out | ios::binary); // ios::binary pas connnu partout - $CHECK$ Reza 13/02/98 #else s = new ofstream(flnm.c_str(),ios::out ); #endif #endif PPersistMgr::Reset(); PutBytes("PEIDA-PPersistFile V5 ",32); PutBytes(bigEndian ? "BIG-ENDIAN " : "LITTLE-ENDIAN ",32); // ---- On ecrit la date de creation a partir de V5 Date today; string stod = today.DateStr(Date::kLocalTime) + "#" + today.TimeStr(Date::kLocalTime); char buff[36]; int l = stod.length(); if (l < 33) strcpy(buff, stod.c_str()); else strncpy(buff, stod.c_str(), 33); for(int i=l+1; i<32; i++) buff[i] = ' '; buff[32] = '\0'; PutBytes(buff, 32); // Fin d'ecriture de date -------- previous = -1; // Pas de Tag precedant numTag = 0; } POutPersist::~POutPersist() { PutI8(previous); PutI4(numTag); delete s; PPersistMgr::Reset(); } int_4 POutPersist::WriteTag(int_4 key, char const * nom) { int_8 nexprev; uint_2 l; int ll; char pad[4] = {'\0', '\0', '\0', '\0'}; #ifdef STREAMPOS_IS_CLASS nexprev = s->tellp().offset(); #else nexprev = s->tellp(); #endif // On ecrit dans l'ordre // - La position du tag precedant (-1 s'il n'y en a pas) // - Le numero de tag ( qui s incremete automatiquement ) // - La cle utilisateur (key) // - La longueur du nom // - le nom lui-meme (Avec un pad afin de faire nom+longueur=multiple de 4) PutI8(previous); PutI4(numTag); PutI4(key); if (nom==NULL) { PutU2(0); PutU2(0); } else { ll = strlen(nom); if (ll <= 0) { PutU2(0); PutU2(0); } l = (ll <= MAXTAGNAMELEN) ? ll : MAXTAGNAMELEN ; PutU2(l); PutBytes(nom,l+1); ll = 3-((ll+2)%4); PutBytes(pad, ll); } previous = nexprev; numTag++; return((numTag-1)); } //++ // void POutPersist::PutByte(char& c) // void POutPersist::PutBytes(void const* ptr, size_t bytes) // void POutPersist::PutR4 (r_4 result) // void POutPersist::PutR4s (r_4 const* tab, size_t n) // void POutPersist::PutR8 (r_8 result) // void POutPersist::PutR8s (r_8 const* tab, size_t n) // void POutPersist::PutI2 (int_2 result) // void POutPersist::PutI2s (int_2 const* tab, size_t n) // void POutPersist::PutU2 (uint_2 result) // void POutPersist::PutU2s (uint_2 const* tab, size_t n) // void POutPersist::PutI4 (int_4 result) // void POutPersist::PutI4s (int_4 const* tab, size_t n) // void POutPersist::PutU4 (uint_4 result) // void POutPersist::PutU4s (uint_4 const* tab, size_t n) // void POutPersist::PutI8 (int_8 result) // void POutPersist::PutI8s (int_8 const* tab, size_t n) // void POutPersist::PutU8 (uint_8 result) // void POutPersist::PutU8s (uint_8 const* tab, size_t n) // Ecriture de données portables.. Pour chaque type // de données, on peut écrire une valeur, ou un tableau de valeurs. // void POutPersist::PutLine(char const* ptr, size_t len) // Ecriture d'une ligne de texte dans le fichier PPersist. //-- void POutPersist::PutByte(char c) { PutBytes(&c, 1); } void POutPersist::PutBytes(void const* ptr, size_t bytes) { s->write((char const*)ptr, bytes); } void POutPersist::PutR4 (r_4 val) { if (bigEndian != IS_BIG_ENDIAN) bswap4(&val); PutBytes(&val, sizeof(r_4)); } void POutPersist::PutR4s (r_4 const* tab, size_t n) { if (bigEndian == IS_BIG_ENDIAN) PutBytes(tab, n*sizeof(r_4)); else for (unsigned int i=0; iwrite(ptr, len); #ifdef RFIO char cd = PIOP_Delim; s->write(&cd, 1); #else s->put(PIOP_Delim); #endif } // --- Classe d'initialisation de PEIDA++, (PPersistMgr en particulier) int PeidaInitiator::FgInit = 0; PeidaInitiator::PeidaInitiator() { FgInit++; if (FgInit > 1) return; InitFailNewHandler(); #ifdef xx__mac__ //InitToolBox(); //SIOUXSettings.initializeTB = FALSE; SIOUXSettings.autocloseonquit = FALSE; SIOUXSettings.asktosaveonclose = FALSE; SIOUXSettings.showstatusline = TRUE; #endif PPersistMgr::classList = new PPersistMgr::ClassList; PShPersist::objList = new PShPersist::ObjList; #if (!defined(__GNUG__) && !defined(__MWERKS__) && !defined(HPUX)) // pas de bufferisation pour printf cmv 18/3/97 selon E.A. // setvbuf(stdout,NULL,_IOLBF,0); setvbuf(stderr,NULL,_IOLBF,0); setlinebuf(stdout); setlinebuf(stderr); #endif // si var env PEIDA_NOPRTVER definie pas de print if(!getenv("PEIDA_NOPRTVER")) PrintPeidaVersion(); } PeidaInitiator::~PeidaInitiator() { FgInit--; if (FgInit == 0) { delete PPersistMgr::classList; PPersistMgr::classList = NULL; delete PShPersist::objList; PShPersist::objList = NULL; } } double PeidaInitiator::Version(bool fgprt) { if (fgprt) PrintPeidaVersion(); return(PeidaVersion()); } // On met un objet initiator en statique, pour les loaders qui savent // appeler le constructeur des objets statiques Reza 08/98 static PeidaInitiator ppeidainit;