4 Persistent objects can be written and later recovered from a stream in
5 its entirety in a serial manner.
7 The persistence protocol is implemented in class Pstream in the library.
8 User defined persistent types should be derived from the base class PObject.
9 Each object should redefine the following virtual methods:
11 From <AD/persist/pobject.h>
15 virtual TypeId persist_type_id () const;
16 virtual const char * persist_type_name () const;
17 virtual Pistream& persist_read (Pistream&);
18 virtual Postream& persist_write (Postream&) const;
19 # define DEFINE_PERSISTENT_CLASS(ID, NAME) \
20 virtual TypeId persist_type_id () const { return ID; } \
21 virtual const char * persist_type_name () const { return NAME; } \
22 virtual Pistream& persist_read (Pistream&); \
23 virtual Postream& persist_write (Postream&) const;
26 Briefly, method 'persist_type_id()' identifies the type of each object.
27 The TypeId for each user object must be a positive integer and each
28 type must have an unique id. The method 'persist_type_name()' returns
29 a string identifying the type.
31 Method 'persist_read()' and 'persist_write()' are invoked during
32 reading and writing from and to a persistence stream. These methods
33 should not be called by the client directly but are instead called from
34 the persistent stream classes. Being such it is alright to place
35 these virtual methods in the private section of the class.
37 Currently, the persistent stream class heirarchy as structured as follows:
45 Persistent input streams have the following protocol. Notice that
46 an istream must be attached to the Pistream during object construction.
47 An optional memory manager can also be attached to the persistent stream,
48 and if this is supplied then all new storage will be acquired from this
49 memory manager instead of from the operator new.
51 From <AD/persist/pstream.h>
53 class Pistream : public virtual Pstream {
56 Pistream(istream&, Mem&);
59 friend Pistream& operator >> (Pistream&, int&);
60 friend Pistream& operator >> (Pistream&, unsigned int&);
61 friend Pistream& operator >> (Pistream&, short&);
62 friend Pistream& operator >> (Pistream&, unsigned short&);
63 friend Pistream& operator >> (Pistream&, char&);
64 friend Pistream& operator >> (Pistream&, unsigned char&);
65 friend Pistream& operator >> (Pistream&, long&);
66 friend Pistream& operator >> (Pistream&, unsigned long&);
67 friend Pistream& operator >> (Pistream&, float&);
68 friend Pistream& operator >> (Pistream&, double&);
69 friend Pistream& operator >> (Pistream&, const char *&);
70 friend Pistream& operator >> (Pistream&, const unsigned char *&);
71 friend Pistream& operator >> (Pistream&, PObject&);
72 friend Pistream& operator >> (Pistream&, const PObject *&);
75 As such, the persistent streams do not actually perform the actual I/O;
76 it only implements the serialization and marshalling protocol. This extra
77 layer of abstraction provides more flexibility with little performance
78 cost. For example, we can attach any subclass of istream to a Pistream.
80 The serialization protocol performs the following functions:
81 (a) insert type information into the byte streams and decode the type
82 information on the way back.
83 (b) encode pointer sharing information so that the structure of a network
84 of objects is preserved on the receiving end. And finally,
85 (c) encode the information in network byte order so that the output format
86 of an object is architecture independent and portable across platforms
87 (currently floating point numbers are not properly encoded.)
90 The output stream protocol is an exact mirror of the input protocol:
92 class Postream : public virtual Pstream {
97 friend Postream& operator << (Postream&, int);
98 friend Postream& operator << (Postream&, unsigned int);
99 friend Postream& operator << (Postream&, short);
100 friend Postream& operator << (Postream&, unsigned short);
101 friend Postream& operator << (Postream&, char);
102 friend Postream& operator << (Postream&, unsigned char);
103 friend Postream& operator << (Postream&, long);
104 friend Postream& operator << (Postream&, unsigned long);
105 friend Postream& operator << (Postream&, float);
106 friend Postream& operator << (Postream&, double);
107 friend Postream& operator << (Postream&, const char *);
108 friend Postream& operator << (Postream&, const unsigned char *);
109 friend Postream& operator << (Postream&, const PObject&);
110 friend Postream& operator << (Postream&, const PObject *);
113 Each type of persistent objects makes use of an 'object factory' to
114 create objects of each type. Object factories are created and registered
115 using the template PObjectFactory:
117 From <AD/persist/pfactory.h>
120 class PObjectFactory {
122 PObjectFactory(TypeId);
123 virtual PObject * create_object() { return new T; }
126 Here's an example: persistent classes Foo and Bar, where Bar is
127 a subclass of Foo, can be defined in the following manner:
131 class Foo : public PObject {
132 int temp; // temp is not a persistent member
134 int integer; // the rest are persistent
140 DEFINE_PERSISTENT_CLASS(1, "Foo")
143 class Bar : public Foo {
147 DEFINE_PERSISTENT_CLASS(2, "Bar");
153 // Create an object factory for each persistent type.
154 // The supplied type id for each factory must match that
157 PObjectFactory<Foo> foo_factory(1);
158 PObjectFactory<Bar> bar_factory(2);
160 Note the use of the macro DEFINE_PERSISTENT_CLASS to save work.
161 The class specific read and write methods are defined as follows:
163 Postream& Foo::persist_write(Postream& f)
166 for (int i = 0; i < 10; i++)
172 Pistream& Foo::persist_read(Pistream& f)
175 for (int i = 0; i < 10; i++)
181 Postream& Bar::persist_write(Postream& f)
182 { Foo::persist_write(f);
187 Pistream& Bar::persist_read(Pistream& f)
188 { Foo::persist_read(f);
193 Notice that all the persistent members must be read and written in
194 the same order. The derivation of each persist_write() and persist_read()
195 methods is very similar to the trace() method for the garbage collector,
196 except that in this case we have to read and write all members, not just
199 As we have mentioned, all sharing between pointers are preserved
200 in the persistent encoding, so it is safe to store general graph
201 like structures (i.e. even cyclic structures.)
203 While reading from a persistent stream, the storage for pointers to
204 PObject's are allocated by calling the appropriate object factories
205 (e.g. next and last members in class Foo), which in turns calls the
206 default new method for the class. This, by default, is simply
207 ::operator new(). It is up to the implementor for each class to override
208 this method if other storage management strategy is needed.
210 One final note about strings (char * and unsigned char *): the storage
211 for a string is allocated on the fly during reading. By default, this
212 storage is allocated by calling 'new char []'. If instead a memory manager
213 is supplied to the Pistream's constructor, then memory will be allocated
214 from the memory manager. All storage allocated are not kept track of in
215 any way by the persistent streams, and it is up to the user to release
216 them when they are not longer needed.
218 As a convenience, all garbage collectors are subclassed from GC, which in
219 turns is subclassed from Mem, so it is possible to use garbage collection
220 if it is difficult to manually manage the allocated storage.