Data Member is a Value-based or Pointer-based Collection
Associations can also be of the one-to-many type in which an object is associated with some set of other objects of like type. Just like one-to-one associations, one-to-many associations can either store objects directly or can store pointers to objects. For example, a residential_property might contain a number of rooms (all of the same class), and an agent might list several instances of real_property (of different types).
The examples below show how these associations can be serialized if they are implemented using a value-based and pointer-based collection.
The next example uses a polymorphic attribute in the real_property class. The first two code listings show the new header and source files, respectively. The real_property class has been changed from the previous section to include a heater attribute. This attribute is a pointer to a heating_system type, which is a base class. Generally, the attribute holds either a pointer to either a baseboard or a forced_air object (see examples\serial\association\heating.h).
// examples\serial\association\real_property.h
class real_property
{
RW_DECLARE_VIRTUAL_STREAM_FNS(real_property)
public:
real_property () : heater_(0) { }
virtual ~real_property() {
delete heater_;
}
real_property (const RWCString& address,
const RWCString& size)
: address_(address), size_(size), heater_(0) {
}
real_property (const RWCString& address,
const RWCString& size,
heating_system* heater);
: address_(address), size_(size), heater_(heater) {
}
bool
operator== (const real_property& prop) const {
return address_ == prop.address_;
}
private:
RWCString address_;
RWCString size_;
heating_system* heater_; // 1
};
// Allow object streaming
RW_DECLARE_STREAMABLE_AS_SELF(real_property)
RW_DECLARE_STREAMABLE_POINTER(real_property)
The following code sample is from: examples\serial\association\real_property.cpp.
// Define how real_property will be serialized
RW_BEGIN_STREAM_CONTENTS(real_property)
{
RW_STREAM_ATTR_MEMBER(address,address_)
RW_STREAM_ATTR_MEMBER(size,size_)
RW_STREAM_ATTR_MEMBER(heater,heater_) // 1
}
RW_END_STREAM_CONTENTS
// Allow a reference to a real_property to be serialized
RW_DEFINE_STREAMABLE_AS_SELF(real_property)
RW_DEFINE_STREAMABLE_POINTER(real_property)
Next is the one-to-many value-based association. The residential class is changed from the previous section to include a list of rooms. The room class is itself a nested class.
// examples\serial\association\residential.h
RW_DECLARE_STREAMABLE_VAL_SEQUENCE(RWTValSlist) // 1
class residential : public real_property
{
RW_DECLARE_VIRTUAL_STREAM_FNS(residential)
public:
class room // 2
{
RW_DECLARE_VIRTUAL_STREAM_FNS(room) // 3
public:
room() { }
room(const RWCString& type, const RWCString& size)
: type_(type), size_(size) {
}
private:
RWCString type_;
RWCString size_;
};
residential () { }
virtual ~residential() { }
residential(const RWCString& address, const RWCString& size,
long footage, const RWTValSlist<room>& rooms =
RWTValSlist<room>())
: real_property(address, size),
footage_(footage), rooms_(rooms)
{}
residential (const RWCString& address, const RWCString& size,
long footage, heating_system* heater,
const RWTValSlist<room>& rooms =
RWTValSlist<room>())
: real_property(address,size,heater), footage_(footage),
rooms_(rooms)
{}
bool operator== (const residential& prop) const {
return real_property::operator==(prop);
}
void addRoom(const room& r) { // 4
rooms_.append(r);
}
private:
long footage_;
RWTValSlist<room> rooms_; // 5
};
typedef residential::room residential_room; // 6
RW_DECLARE_STREAMABLE_AS_SELF(residential)
RW_DECLARE_STREAMABLE_AS_BASE(residential,real_property)
RW_DECLARE_STREAMABLE_POINTER(residential)
RW_DECLARE_STREAMABLE_AS_SELF(residential_room)
RW_DECLARE_STREAMABLE_POINTER(residential_room) // 7
// examples\serial\association\residential.cpp
RW_BEGIN_STREAM_CONTENTS(residential)
{
RW_STREAM_PARENT(real_property)
RW_STREAM_ATTR_MEMBER(footage, footage_)
RW_STREAM_ATTR_MEMBER(rooms, rooms_) // 1
}
RW_END_STREAM_CONTENTS
RW_DEFINE_STREAMABLE_AS_SELF(residential)
RW_DEFINE_STREAMABLE_AS_BASE(residential, real_property)
RW_DEFINE_STREAMABLE_POINTER(residential)
RW_BEGIN_STREAM_CONTENTS(residential_room) // 2
{
RW_STREAM_ATTR_MEMBER(type, type_)
RW_STREAM_ATTR_MEMBER(size, size_)
}
RW_END_STREAM_CONTENTS
RW_DEFINE_STREAMABLE_AS_SELF(residential_room)
RW_DEFINE_STREAMABLE_POINTER(residential_room)
The next example uses a one-to-many pointer-based association. An agent class is defined that holds a list of properties. Because the properties may be any one of residential, rural, or rural_land, the list is a pointer-based polymorphic collection.
First, define the agent class.
// examples\serial\association\agent.h
RW_DECLARE_STREAMABLE_PTR_SEQUENCE(RWTPtrSlist)
class agent : public real_property
{
RW_DECLARE_VIRTUAL_STREAM_FNS(agent)
public:
agent () { }
virtual ~agent() {
RWTValSet<real_property*, std::less<real_property*> > properties;
RWTPtrSlist<real_property>::iterator iter = listing_.begin();
for (; iter != listing_.end(); ++iter) {
if (properties.insert(*iter)) {
delete *iter;
}
}
}
agent(const RWCString& name,
const RWTPtrSlist<real_property>& listing =
RWTPtrSlist<real_property>())
: name_(name), listing_(listing)
{}
void addListing(real_property* prop) {
listing_.append(prop);
}
private:
RWCString name_; // name of agent
RWTPtrSlist<real_property> listing_; // 1
};
// Allow object streaming of agent class
RW_DECLARE_STREAMABLE_AS_SELF(agent)
RW_DECLARE_STREAMABLE_POINTER(agent)
Next, define the
streamContents() function. Just as with
RWTValSlist<T>, the framework provides an inserter and extractor for the
RWTPtrSlist<T> class, so all that’s needed is to use the macro
RW_DECLARE_STREAMABLE_PTR_SEQUENCE(RWTPtrSlist).
// examples\serial\association\agent.cpp.
// Define how agent property will be serialized
RW_BEGIN_STREAM_CONTENTS(agent)
{
RW_STREAM_ATTR_MEMBER(name, name_)
RW_STREAM_ATTR_MEMBER(listing, listing_)
}
RW_END_STREAM_CONTENTS
RW_DEFINE_STREAMABLE_AS_SELF(agent)
RW_DEFINE_STREAMABLE_POINTER(agent)
The following shows the new classes in action.
// examples\serial\association\listing.cpp
// 1
residential* real1 = new residential
("1980 Main St. Corvallis, Oregon","50X100",1200, new baseboard());
real1->addRoom(residential::room("living", "18x20"); // 2
real1->addRoom(residential::room("bedroom", "14x16");
real1->addRoom(residential::room("bath", "8x12");
residential* real2 = new residential
("1980 Main St. Corvallis, Oregon", "50X100", 1200,new forced_air());
rural* rural1 = new rural
("19980 Bugs Bunny Rd. Corvallis, Oregon", "20 acres", 1200, 10);
real_property* rural2 = new rural_land
("19990 Bugs Bunny Rd. Corvallis, Oregon", "10 acres", 5);
agent a1("George"); // 3
a1.addListing(real1);
a1.addListing(real2);
a1.addListing(rural1);
a1addListing(rural2);
a1.addListing(real1); // 4
{
// open a file for output
ofstream fout("listing.dat");
// Create an Compact object output stream using the
// standard file stream
RWpostream postr(fout);
RWObjectOutputStream out = RWCompactObjectOutputStreamImp::
make(RWDataToVirtualOutputStreamImp::
make(postr));
out << a1;
}
// open the file for input
ifstream fin("listing.dat");
// Create a Compact object input stream using the standard file stream
RWpistream pistr(fin);
RWObjectInputStream in = RWCompactObjectInputStreamImp::
make(RWDataFromVirtualInputStreamImp::
make(postr));
agent a2; // 5
in >> a2;