Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

archive.hpp

00001 /************************************************************************************
00002  *    This file is part of the MynahSA streaming and archiving toolkit              *
00003  *    Copyright (C) 2006 Mynah-Software Ltd. All Rights Reserved.                   *
00004  *                                                                                  *
00005  *    This program is free software; you can redistribute it and/or modify          *
00006  *    it under the terms of the GNU General Public License, version 2               *
00007  *    as published by the Free Software Foundation.                                 *
00008  *                                                                                  *
00009  *    This program is distributed in the hope that it will be useful,               *
00010  *    but WITHOUT ANY WARRANTY; without even the implied warranty of                *
00011  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
00012  *    GNU General Public License for more details.                                  *
00013  *                                                                                  *
00014  *    You should have received a copy of the GNU General Public License along       *
00015  *    with this program; if not, write to the Free Software Foundation, Inc.,       *
00016  *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.                   *
00017  *                                                                                  *
00018  ************************************************************************************/
00019 
00020 #ifndef __archive_hpp
00021 #define __archive_hpp
00022 
00023 #include <mynahsa/spimpl.hpp>
00024 
00025 #include <string>
00026 #include <vector>
00027 #include <list>
00028 #include <utility>
00029 #include <map>
00030 #include <set>
00031 
00032 #include <mynahsa/exceptionbase.hpp>
00033 
00034 #include <mynahsa/type_traits.hpp>
00035 
00036 namespace MynahSA { 
00037   class IoBase;
00038 
00039 
00043   class Archive { 
00044   public:
00045   
00050     class ArchiveConstructor { 
00051     public:
00053       ArchiveConstructor() {
00054       }
00055   
00057       ~ArchiveConstructor() {
00058       }
00059   
00061       virtual SHARED_PTR<IoBase> operator()() const = 0;
00062     };
00063  
00064   
00066     Archive();
00067     
00069     Archive(const Archive& ar);
00070     
00072     Archive(const std::map<std::string, SHARED_PTR<Archive::ArchiveConstructor> >& cons);
00073   
00075     virtual ~Archive();
00076   
00077   
00083     void registerConstructor(const std::string& name, const SHARED_PTR<ArchiveConstructor>& cons) {
00084       _constructors[name] = cons;
00085     }
00086  
00088     const std::map<std::string, SHARED_PTR<ArchiveConstructor> >& getConstructors() const { 
00089       return _constructors; 
00090     }
00091     
00092     // must treat int / bool / float / double / char
00093   
00095     virtual Archive& operator&(long long&) = 0;
00096     
00098     virtual Archive& operator&(unsigned long long&) = 0;
00099   
00101     virtual Archive& operator&(unsigned int&) = 0;
00102   
00104     virtual Archive& operator&(int&) = 0;
00105     
00107     virtual Archive& operator&(short&) = 0;
00108     
00110     virtual Archive& operator&(unsigned short&) = 0;
00111     
00113     virtual Archive& operator&(char&) = 0;
00114     
00116     virtual Archive& operator&(unsigned char&) = 0;
00117     
00119     virtual Archive& operator&(bool&) = 0;
00120     
00122     virtual Archive& operator&(float&) = 0;
00123     
00125     virtual Archive& operator&(double&) = 0;
00126   
00128     virtual Archive& operator&(std::string&) = 0;
00129   
00131     template<class T>
00132     Archive& operator&(std::vector<T>& v) { 
00133       // these statements work around the issues that this method is used for both read and write
00134       // step 1: copy the size
00135       int size = v.size();
00136       // step 2: archive the size - NOTE:
00137       //        In the IArchive case - size will be overwritten
00138       //        In the OArchive case - size will be v.size()
00139       (*this) & size;
00140       //        In IArchive case, resize the vector appropriately
00141       if (size > v.size()) {
00142         v.resize(size);
00143       }
00144       // now recover the data
00145       for (unsigned int i=0; i<v.size(); i++) { 
00146         (*this) & v[i];
00147       }
00148       return *this;
00149     }
00150   
00152     template<class T>
00153     Archive& operator&(std::list<T>& l) { 
00154       // This method contains more trickery like the vector storage routine above.  The key difference
00155       // here is that the symmetric read/write operator used for the vector (operator[]) is unavailable
00156       // for lists - even if it were available, the cost would be order N in the list length each time
00157       // a list item were accessed.  To circumvent this problem we change the loop control to use
00158       // iterators as each item has a symmetric operator defined on it - Archive's operator&
00159       int size = l.size();
00160       (*this) & size;
00161       if (size > l.size()) { 
00162         l.resize(size);
00163       }
00164       for (typename std::list<T>::iterator lit = l.begin();
00165           lit != l.end();
00166           ++lit) { 
00167         (*this) & (*lit);
00168       }
00169       return *this;
00170     }
00171     
00173     template<class a_type, class b_type>
00174     Archive& operator&(std::pair<a_type, b_type>& p) { 
00175       (*this) & p.first;
00176       (*this) & p.second;
00177       return *this;
00178     }
00179     
00180     
00182     template<class value_type>
00183     Archive& operator&(std::set<value_type>& s) { 
00184       // Without any symmetric read/write operator we are forced to split the archive method into two
00185       // paths for std::set.  We rely on a virtual member getArchiveMode() to inform us of the data direction.
00186       // If we are reading, we use the read path, otherwise, the write path.
00187       //
00188       // When reading, we recover the number of elements first, then for each element, inarchive a 
00189       // value_type and insert that entry into the set.
00190       // 
00191       // When writing, we simply store the size, and loop over the entire map, writing each 
00192       // value_type onto the archive
00193       if (getArchiveMode() == ARCHIVE_READ) { 
00194         // step 1: clear the current map
00195         s.clear();
00196         // step 2: recover the size off the stream
00197         int size = 0;
00198         (*this) & size; // get the amount of data off the stream
00199         // step 3: for each element on the stream -
00200         for (unsigned int i=0; i < size; i++) { 
00201           // step 3a: recover an element
00202           value_type element;
00203           (*this) & element;
00204           // step 3b: store element into map.
00205           s.insert(element);
00206         }
00207       } else { 
00208         // step 1: archive the size
00209         int size = s.size();
00210         (*this) & size;  
00211         // step 2: archive each element
00212         for (typename std::set<value_type>::const_iterator sit = s.begin();
00213             sit != s.end();
00214             ++sit) { 
00215           value_type element = (*sit);  // unfortunate copy
00216           (*this) & element;  // use std::pair archiver to store element
00217         }
00218       }
00219       return *this;
00220     }
00221     
00222     
00224     template<class index_type, class value_type>
00225     Archive& operator&(std::map<index_type, value_type>& m) { 
00226       // Without any symmetric read/write operator we are forced to split the archive method into two
00227       // paths for std::map.  We rely on a virtual member getArchiveMode() to inform us of the data direction.
00228       // If we are reading, we use the read path, otherwise, the write path.
00229       //
00230       // When reading, we recover the number of elements first, then for each element, inarchive a 
00231       // pair of index_type, value_type and insert that entry into the map.
00232       // 
00233       // When writing, we simply store the size, and loop over the entire map, writing each 
00234       // std::pair<index_type, value_type> onto the archive
00235       if (getArchiveMode() == ARCHIVE_READ) { 
00236         // step 1: clear the current map
00237         m.clear();
00238         // step 2: recover the size off the stream
00239         int size = 0;
00240         (*this) & size; // get the amount of data off the stream
00241         // step 3: for each element on the stream -
00242         for (unsigned int i=0; i < size; i++) { 
00243           // step 3a: recover an element
00244           std::pair<index_type, value_type> element;
00245           (*this) & element;
00246           // step 3b: store element into map.
00247           m.insert(element);
00248         }
00249       } else { 
00250         // step 1: archive the size
00251         int size = m.size();
00252         (*this) & size;  
00253         // step 2: archive each element
00254         for (typename std::map<index_type, value_type>::const_iterator mit = m.begin();
00255             mit != m.end();
00256             ++mit) { 
00257           // this copy is unfortunate - it seems that std::map reinterprets std::string as std::basic_string<char, ...> which does not match our stream operator for std::string when compiling with GCC
00258     //BMS20060611 - fix for Solaris compiler, copy both parameters.
00259     //  NOTE: the desired effect here may be achieveable with a
00260     //        reinterpret_cast and/or const cast, and could avoid copying.
00261           std::pair<index_type, value_type> element( (*mit).first, (*mit).second );
00262           (*this) & element;  // use std::pair archiver to store element
00263         }
00264       }
00265       return *this;
00266     }
00267   
00269     template<class index_type, class value_type>
00270     Archive& operator&(std::multimap<index_type, value_type>& m) { 
00271       // Without any symmetric read/write operator we are forced to split the archive method into two
00272       // paths for std::map.  We rely on a virtual member getArchiveMode() to inform us of the data direction.
00273       // If we are reading, we use the read path, otherwise, the write path.
00274       //
00275       // When reading, we recover the number of elements first, then for each element, inarchive a 
00276       // pair of index_type, value_type and insert that entry into the map.
00277       // 
00278       // When writing, we simply store the size, and loop over the entire map, writing each 
00279       // std::pair<index_type, value_type> onto the archive
00280       if (getArchiveMode() == ARCHIVE_READ) { 
00281         // step 1: clear the current map
00282         m.clear();
00283         // step 2: recover the size off the stream
00284         int size = 0;
00285         (*this) & size; // get the amount of data off the stream
00286         // step 3: for each element on the stream -
00287         for (unsigned int i=0; i < size; i++) { 
00288           // step 3a: recover an element
00289           std::pair<index_type, value_type> element;
00290           (*this) & element;
00291           // step 3b: store element into map.
00292           std::pair<const index_type, value_type> cElement(element.first, element.second);
00293           m.insert(cElement);                               // NOTE: implicit use of copy constructor
00294         }
00295       } else { 
00296         // step 1: archive the size
00297         int size = m.size();
00298         (*this) & size;  
00299         // step 2: archive each element
00300         for (typename std::multimap<index_type, value_type>::const_iterator mit = m.begin();
00301             mit != m.end();
00302             ++mit) { 
00303           // this copy is unfortunate - it seems that std::map reinterprets std::string as std::basic_string<char, ...> which does not match our stream operator for std::string when compiling with GCC
00304           //BMS20060611 - fix for Solaris compiler, copy both parameters.
00305           //  NOTE: the desired effect here may be achieveable with a
00306           //        reinterpret_cast and/or const cast, and could avoid copying.
00307           std::pair<index_type, value_type> element( (*mit).first, (*mit).second );
00308           (*this) & element;  // use std::pair archiver to store element
00309         }
00310       }
00311       return *this;
00312     }
00313   
00314   
00315 
00316 
00321     template<class T>
00322     Archive& operator&(SHARED_PTR<T>& ptr) { 
00323       return archiveSP(ptr, fundamental_t<T>());
00324     }
00325 
00326 
00343     template<class T>
00344     Archive& operator&(T& x) {
00345       // a bit of template meta-programming, 
00346       serializeArray(x, MynahSA::array_t<T>());
00347       return *this;
00348     }
00349     
00350     
00351 
00352     
00353     
00381     void clearUPR();
00382     
00386     template<class T>
00387     void touchPtr(const SHARED_PTR<T>& ptr) { 
00388       SHARED_PTR<void> value(STATIC_PTR_CAST<void>(ptr)); // fetch pointer value
00389       std::map<SHARED_PTR<void>, unsigned int>::iterator mit = _ptrToUPR.find(value);  // find its universal pointer value
00390       if (mit != _ptrToUPR.end()) { 
00391         unsigned int upr = (*mit).second;
00392         _ptrToUPR.erase(mit);  // get rid of the upr and pointer associated
00393         std::map<unsigned int, SHARED_PTR<void> >::iterator mit2 = _uprToPtr.find(upr);
00394         if (mit2 != _uprToPtr.end()) {
00395           _uprToPtr.erase(mit2);
00396         }
00397       }
00398     }
00399     
00400   protected:
00401     enum ArchiveMode { ARCHIVE_READ, ARCHIVE_WRITE };
00404     virtual ArchiveMode getArchiveMode() const =0;
00405     
00406     
00407   private:
00408   
00410     template<class T>
00411     void serializeArray(T& x, const false_t&) {
00412       // a bit of template meta-programming, 
00413       serializeTerm(x, MynahSA::enum_t<T>());
00414     }
00415     
00417     template<class T>
00418     void serializeArray(T& x, const true_t&) { 
00419       for (unsigned int i=0; i<sizeof(T); i++) { 
00420         (*this) & x[i];
00421       }
00422     }  
00423  
00424   
00426     template<class T> 
00427     void serializeTerm(T& x, const true_t&) { 
00428       (*this) & ((int&) x);
00429     }
00430     
00432     template<class T>
00433     void serializeTerm(T& x, const false_t&) { 
00434       x.serialize(*this);
00435     }
00436   
00437   
00439     template<class T>
00440     Archive& archiveSP(SHARED_PTR<T>& instance, const true_t&) {
00441       
00442       if (getArchiveMode() == ARCHIVE_READ) { 
00443         // read path
00444         
00445         // step 1: Determine if the sender is sending a UPR or a real object 
00446         bool useUPR;
00447         (*this) & useUPR;
00448         if (useUPR) { 
00449           // fetch the UPR
00450           unsigned int upr;
00451           (*this) & upr;
00452           std::map< unsigned int, SHARED_PTR<void> >::const_iterator mit = _uprToPtr.find(upr);
00453           
00454           if (mit == _uprToPtr.end()) {
00455             throw ExceptionBase("Archive::operator&(SHARED_PTR<T>&) - recovery error: Unique Pointer Reference does not exist.");
00456           }
00457           
00458           // ok, UPR was found - perform shared pointer cast - return pointer
00459           instance = STATIC_PTR_CAST<T>( (*mit).second );
00460         } else { 
00461           // We're not receiving a UPR; we must recover an entire object
00462           
00463           // constructor function performs archive recovery operation
00464           instance = SHARED_PTR<T>(new T);
00465           
00466           // now - we must store the instance; as we may require this again later, by upr
00467           // we do not have a complex protocol for agreeing on UPRs between transmitter and receiver, rather,
00468           // the algorithm is sequential increment - therefore, everytime we transmit an object, we increment the
00469           // transmitter's UPR counter, and everytime we receive a new object we increment the UPR count...
00470           // Both algorithms play out in sequence on the transmitter and receiver so there are no problems
00471           // with UPRs getting out of sequence.
00472           
00473           SHARED_PTR<void> internalPointer = STATIC_PTR_CAST<void>(instance);  // convert to a void ptr
00474           // store on pointer to upr map
00475           _ptrToUPR[internalPointer] = _nextUPR;
00476           // store on upr to pointer map
00477           _uprToPtr[_nextUPR] = internalPointer;
00478           // increment upr count
00479           ++_nextUPR;
00480           
00481           // recover the object from the stream
00482           (*this) & (*instance);  // restore object pointed to by fundamental type pointer
00483         }
00484       } else { 
00485         // write path
00486         
00487         // step 1: determine if we've previously transmitted this object
00488         SHARED_PTR<void> internalPointer = STATIC_PTR_CAST<void>(instance); // convert to void ptr
00489         
00490         // step 2: find pointer on ptrToUPR map
00491         std::map< SHARED_PTR<void>, unsigned int >::const_iterator mit = _ptrToUPR.find(internalPointer);
00492         if (mit != _ptrToUPR.end()) { 
00493           // transmit the UPR and do not transmit the object again
00494           bool trueBool = true;
00495           (*this) & trueBool;  // transmit true to indicate we are transmitting a UPR
00496           unsigned int upr = (*mit).second;
00497           (*this) & upr;       // transmit the unique pointer reference
00498         } else { 
00499           bool falseBool = false;
00500           (*this) & falseBool;  // we're not sending a UPR, but the whole object
00501         
00502           // note: with fundamental_t(s) we don't send a string for the object name
00503 
00504           // now - store the UPR for this object.  We must do this prior to transmitting the object
00505           // if we did not do this in order, we could endup in a recursive loop.
00506           //
00507           // Another way of thinking about this step is that it breaks the graph structure, so back-edges
00508           // pointing at this node are now effectively broken; we won't re-traverse this node.
00509           _ptrToUPR[internalPointer] = _nextUPR;
00510           _uprToPtr[_nextUPR] = internalPointer;
00511           ++_nextUPR;
00512           
00513           // and transmit the instance
00514           (*this) & (*instance);
00515         }
00516       }
00517       return *this;
00518     }
00519   
00520   
00521   
00522   
00524     template<class T>
00525     Archive& archiveSP(SHARED_PTR<T>& instance, const false_t&) {
00526 #ifdef DEBUG
00527       // this is basically an assertion - that instance is a descendant of IoBase
00528       STATIC_PTR_CAST<IoBase>(instance);
00529 #endif      
00530       
00531       if (getArchiveMode() == ARCHIVE_READ) { 
00532         // read path
00533         
00534         // step 1: Determine if the sender is sending a UPR or a real object 
00535         bool useUPR = false;
00536         (*this) & useUPR;
00537         if (useUPR) { 
00538           // fetch the UPR
00539           unsigned int upr;
00540           (*this) & upr;
00541           std::map< unsigned int, SHARED_PTR<void> >::const_iterator mit = _uprToPtr.find(upr);
00542           
00543           if (mit == _uprToPtr.end()) {
00544             throw ExceptionBase("Archive::operator&(SHARED_PTR<T>&) - recovery error: Unique Pointer Reference does not exist.");
00545           }
00546           
00547           // ok, UPR was found - perform shared pointer cast - return pointer
00548           instance = STATIC_PTR_CAST<T>( (*mit).second );
00549         } else { 
00550           // We're not receiving a UPR; we must recover an entire object
00551           std::string name; // get the class name
00552           (*this) & name;  
00553           SHARED_PTR<ArchiveConstructor> iac = _constructors[name];
00554   
00555           if (!iac) {
00556             throw ExceptionBase(std::string("Class name: ")+name+" is not registered");
00557           }
00558           // constructor function performs archive recovery operation
00559           instance = STATIC_PTR_CAST<T>( (*iac)() );
00560           
00561           // now - we must store the instance; as we may require this again later, by upr
00562           // we do not have a complex protocol for agreeing on UPRs between transmitter and receiver, rather,
00563           // the algorithm is sequential increment - therefore, everytime we transmit an object, we increment the
00564           // transmitter's UPR counter, and everytime we receive a new object we increment the UPR count...
00565           // Both algorithms play out in sequence on the transmitter and receiver so there are no problems
00566           // with UPRs getting out of sequence.
00567           
00568           SHARED_PTR<void> internalPointer = STATIC_PTR_CAST<void>(instance);  // convert to a void ptr
00569           // store on pointer to upr map
00570           _ptrToUPR[internalPointer] = _nextUPR;
00571           // store on upr to pointer map
00572           _uprToPtr[_nextUPR] = internalPointer;
00573           // increment upr count
00574           ++_nextUPR;
00575           
00576           // recover the object from the stream
00577           instance->serialize(*this);
00578                     
00579         }
00580       } else { 
00581         // write path
00582         
00583         // step 1: determine if we've previously transmitted this object
00584         SHARED_PTR<void> internalPointer = STATIC_PTR_CAST<void>(instance); // convert to void ptr
00585         
00586         // step 2: find pointer on ptrToUPR map
00587         std::map< SHARED_PTR<void>, unsigned int >::const_iterator mit = _ptrToUPR.find(internalPointer);
00588         if (mit != _ptrToUPR.end()) { 
00589           // transmit the UPR and do not transmit the object again
00590           bool trueBool = true;
00591           (*this) & trueBool;  // transmit true to indicate we are transmitting a UPR
00592           unsigned int upr = (*mit).second;
00593           (*this) & upr;       // transmit the unique pointer reference
00594         } else { 
00595           bool falseBool = false;
00596           (*this) & falseBool;  // we're not sending a UPR, but the whole object
00597         
00598           // prevent problems when converting value into reference
00599           std::string copyRef = instance->ioName();         // transmit object name
00600           (*this) & copyRef; // store the io name
00601 
00602           
00603           // now - store the UPR for this object.  We must do this prior to transmitting the object
00604           // if we did not do this in order, we could endup in a recursive loop.
00605           //
00606           // Another way of thinking about this step is that it breaks the graph structure, so back-edges
00607           // pointing at this node are now effectively broken; we won't re-traverse this node.
00608           _ptrToUPR[internalPointer] = _nextUPR;
00609           _uprToPtr[_nextUPR] = internalPointer;
00610           ++_nextUPR;
00611           
00612           // and transmit the instance
00613           instance->serialize( (*this) );
00614           
00615         }
00616       }
00617       return *this;
00618     }
00619   
00620   
00621   
00622   
00623   
00624   
00625   
00626   
00627   
00628   
00629   
00631     std::map<std::string, SHARED_PTR<ArchiveConstructor> > _constructors;
00632     
00634     std::map< SHARED_PTR<void>, unsigned int > _ptrToUPR;
00635     
00637     std::map< unsigned int, SHARED_PTR<void> > _uprToPtr;
00638     
00640     unsigned int _nextUPR;
00641   
00642   };
00643 };
00644 
00645 
00646 
00647 #endif