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
};
 
//1 A pointer has been added to the base class heating_system. This attribute can hold a pointer to either of the derived classes (baseboard and forced_air) as shown in examples\serial\association\heating.h.
 
// 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
 
//1 Code has been added to handle the heater attribute.
 
// 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
//1 Use this macro to get streaming operators for RWTValSlist.
//2 Because the class room is nested, it must be treated differently later.
//3 Declare the streamContents() function as with any other class.
//4 This function allows you to add a room to the list of rooms held by the residential property. You can build up a list of rooms by calling this function repeatedly.
//5 The list of rooms. The framework provides inserters and extractors for Rogue Wave collection classes, so nothing extra is required.
//6 This typedef is necessary because the macros used to define the object streaming mechanism cannot deal with things like the scope resolution operator. See External Serialization of Templates and Nested Classes for more details.
//7 Again for the nested class (using the typedef).
 
// 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)
//1 The necessary macro invocation has been added to deal with our new rooms attribute.
//2 The definition of the streamContents() function has been added for the nested class type residential::room. Note the required use of the typedef from the header file.
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
 
};
//1 A pointer-based collection of real_property.
 
// 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;
//1 Create several objects derived from real_property.
//2 Add some rooms.
//3 Create an agent object and populate its listing with properties.
//4 Add the same listing twice to demonstrate the isomorphic properties of object streaming.
//5 Create another agent and populate it by reading in from the file.