#ifndef PERSIST_H #define PERSIST_H /////////////////////////////////////////////////////////////////////// // persist.h - provides persist interface and a template-based // // ver 2.0 function to read objects from persistent store // // // // Language: Visual C++, ver 6.0, SP 5 // // Platform: Dell Dimension 8100, Win 2000 Pro, SP 2 // // Application: CSE691/791 - SWM&A Demonstration // // Author: Jim Fawcett, CST 2-187, Syracuse Univ // // (315) 443-3948, jfawcett@twcny.rr.com // /////////////////////////////////////////////////////////////////////// /* Module Operations: ================== The persist class defines an interface for objects that can be stored and retrieved from a persistent storage stream, typically a file. The users of the persist interface will also derive from the creator class policy. To use persist you declare a class publically derived from persist and define the following pure virtual functions, as protected members: void do_write(std::ostream &toStore); void do_read(std::istream &fromStore); You also must define two public static member functions: static B* create() { return new D; } static classID id() { return "D"; } Here B is the base class you want to users to access, either this class, a base of this class providing a protocol, or creator. D is the name of the class you want to create. An easy way to provide these functions is to also inherit from creator, Using Public Interface of persist ================================= class myClass : public persist { ... } // declare class as above ofstream toStore("objects.db"); // open storage stream myClass obj; // create an object to store toStream << obj; // store it toStream.close(); ifstream fromStore("objects.db"); // open storage stream myClass *pMyClass = ReadObject(fromStore); // read object back out if(pMyClass) // is good read? pMyClass->memberFunc(); // use pointer to new object delete pMyClass; // deallocate new object fromStore.close(); */ // ///////////////////////////////////////////////////////////////////////// // build process // ///////////////////////////////////////////////////////////////////////// // required files: // // persist.h, persist.cpp, creatable.h, creatable.cpp // // // // compiler command: // // cl /GX /GR /DTEST_PERSIST persist.cpp creatable.cpp // // // // note: /GR enables RTTI which we need in order to use typeid(). // // In the IDE you can enable RTTI on the Project Settings // // menu. Select C/C++ tab and select C++ language from the // // category combo box. Check the box for enabling RTTI. // // If you provide a protocol base class, or only store on type // // of object you will not need to use dynamic_cast. // // // ///////////////////////////////////////////////////////////////////////// /* Maintenance History =================== ver 1.0 : 02 Dec 01 - first release Design Notes: ============= It would be easy to extend this demonstration to implement an object database. All we would have to do is to generate a unique identifier for each object, and store the object in a file of that name, instead of a single file, like we do here. You register object's create function with the unique identifier and retrieve polymorphically using the identifier. */ // #include "creatable.h" #include #include #include ///////////////////////////////////////////////////////////////////////// // persist class - supports object persistance class persist { public: void read(std::istream &fromStore); void write(std::ostream &toStore) const; virtual ~persist() { }; protected: virtual void do_read(std::istream &fromStore) = 0; virtual void do_write(std::ostream &toStore) const = 0; }; //----< insertion operator for storing objects onto a stream >----------- inline std::ostream& operator<<(std::ostream &toStore, const persist &p) { p.write(toStore); return toStore; } //----< extraction operator for retrieving objects >--------------------- // // clients generally use the readObject function, below, instead // of extracting objects from the store, as readObject provides // a polymorphic extraction - see the test stub for an example inline std::istream& operator>>(std::istream &fromStore, persist &p) { p.read(fromStore); return fromStore; } // //----< read an object from store >-------------------------------------- // // Note that the user does not specify, or have to know, the type of // the object retrieved, as long as the object's base provides a // protocol template T* readObject(std::istream &in) { if(in.good()) { ///////////////////////////////////////////////////////////////// // read the classID from storage to figure out the object's type classID id; in >> id; ///////////////////////////////////////////////////////////////// // now create an uninitialized object of that type objFactory factory; T *pUser = factory.makeObj(id); ///////////////////////////////////////////////////////////////// // finally, read the state of the object from storage if(pUser) pUser->read(in); return pUser; } return NULL; } #endif