|
|
For some user-defined types, inserters and extractors may already exist; for such types, we only need check that the routines have the required semantics before we ``hook'' them into G2++ via attributes or defaults. For other user-defined types, however, it will be necessary to write the inserters and extractors ourselves.
According to List(C++), for example:
friend ostream& operator<<(ostream& os,List<>& x);
Inserts an ASCII representation of x into os. The representation consists of (1) an open parenthesis followed by (2) the elements of x separated by commas followed by (3) a close parenthesis.
Since the output consists entirely of printable characters, this operator is acceptable and we may omit the get attribute when defining USER type List. List(C++) does not specify an extractor, however, so we would need to provide one ourselves.
For simplicity, let's go back to the Time example. Since Time provides an inserter but not an extractor, we have two choices:
For example, representing a value by its ASCII-fied core image may be fast, but is certainly a bad idea since it locks both environments into using the same (1) machine architecture and (2) implementation of class T.
Two external representations come to mind:
After considering these tradeoffs, we will choose the second representation. Since the existing Time inserter produces the first representation, we will supply put and get attributes naming two functions, to be declared in a file called Timeio.h:
usr.g
Time USER
.header Time.h # class definition
.header Timeio.h # inserter, extractor
.put Tput # inserter
.get Tget # extractor
.null Time::MIN
Timeio.h
#include <Time.h>
#include <iostream.h>
ostream& Tput(ostream& os,const Time& t);
istream& Tget(istream& is,Time& t);
Next, we write the operation definitions, which turn out to be surprisingly simple:
Timeio.c
#include "Timeio.h"
ostream& Tput(ostream& os,const Time& t){
os << t.make_time_t();
return os;
}
istream& Tget(istream& is,Time& t){
long x;
is >> x;
t=make_time(x);
return is;
}