1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
|
itemization(
it() First, the necessary class interfaces are defined. The existence
of a class tt(Date) is assumed, containing overloaded operators
like tt(<) and tt(>) to compare dates.
To start with, we present the interface of the class
tt(Person), omitting all the standard stuff like overloaded assignment
operator, (copy) constructors, etc.:
verb(#include <cstdlib> // for qsort()
class Date;
class Person
{
public:
size_t length() const;
size_t mass() const;
char const *name() const;
char const *city() const;
Date const &birthdate() const;
private:
// all necessary data members
};)
it() Next, the class tt(Person_dbase).
Within this class a tt(struct CmpPerson) is defined, containing
two fields:
itemization(
it() A pointer to a union of compare functions.
As the compare functions are static functions of the class
tt(Person_dbase), pointers to these functions are
indiscernible from pointers to functions at the global
(tt(::)) level. The compare functions return tt(int)s (for
tt(qsort())), and expect two pointers to tt(Person const)
objects. The field tt(persons) expects the two pointers to
tt(Person const) objects. The field tt(voids) is the alternate
interpretation, to be used with tt(qsort()), instead of the
typecast tt((pif2vp)).
it() A field tt(pf) (pointer to access function) of
the nested tt(union Person_accessor).
The types of as many different access functions of the
tt(Person) class as are used in the class are declared in this
union.
Access functions returning tt(int)s, tt(char const *)s and
tt(Date &)s are needed. Consequently, the tt(Person_accessor)
union contains these (three) types.
)
From this tt(CmpPerson) struct a static array tt(cmpPerson[]) is
constructed. It is a tt(static Person_dbase) array, making it possible
for the compare functions to inspect its elements+footnote( The number
of elements of the tt(cmpPerson[]) array is not specified in the
interface: that number is determined at compile-time by the compiler,
when the static variable tt(cmpPerson[]) is initialized.).
Also note the tt(static Listtype selector). This variable
is used by the compare functions to find the actual
tt(Person) access function to be used.
Here, then, is the interface of the class tt(Person_dbase):
verb(class Person_dbase
{
public:
enum Listtype
{
list_by_length,
list_by_mass,
list_by_name,
list_by_city,
list_by_birthday,
};
// ... constructors etc.
void list(Listtype type);
// list the information
private:
struct CmpPerson
{
union Compare_function
{
int (*persons)// comparing two Persons
(Person const *p1, Person const *p2);
int (*voids)// for qsort()
(void const *p1, void const *p2);
}
cmp;
union Person_accessor
{
char const
*(Person::*cp)() const;
int
(Person::*i)() const;
Date const
&(Person::*d)() const;
}
pf; // to Person's access functions
};
static CmpPerson
cmpPerson[];
static Listtype
selector;
static int cmpstr(Person const *p1,
Person const *p2);
static int cmpint(Person const *p1,
Person const *p2);
static int cmpdate(Person const *p1,
Person const *p2);
Person
*pp; // pointer to the info
size_t
n; // number of persons stored.
};)
Next, we define each of the members of the tt(Person_dbase) class
(as far as necessary).
it() The tt(list()) function now only has to do three things:
itemization(
it() The tt(Listtype) parameter is copied to
tt(selector),
it() The function tt(qsort()) is called. Note the
use of the tt(cmpPerson) array to determine which compare
function to use.
it() The information of the tt(Person)objects is
displayed. This part is left for the reader to implement.
)
verb( void Person_dbase::list(Listtype type)
{
selector = type;
qsort(pp, n, sizeof(Person), cmpPerson[type].cmp.voids);
// list the sorted Person-database (to be implemented)
})
it() The array tt(cmpPerson[]) is a static array of tt(CmpPerson)
elements. In this example there are five different ways to sort
the data. Consequently, there are five elements in the array
tt(cmpPerson[]). All these elements can be defined and initialized
by the compiler. No run-time execution time is needed for this.
However, note the form of the declaration: the array is defined in
the scope of the tt(Person_dbase) class. Its elements are
tt(CmpPerson)s, also defined in the scope of the tt(Person_dbase)
class. Hence the double mentioning of tt(Person_dbase).
verb(Person_dbase::CmpPerson
Person_dbase::cmpPerson[] =
{
{ // compare- and access
// function to compare length
cmpint,
Person::length,
},
{ // same for mass
cmpint,
Person::mass,
},
{ // same for name
cmpstr,
Person::name,
},
{ // same for city
cmpstr,
Person::city,
},
{ // same for Date
cmpdate,
Person::birthdate,
},
};)
it() Now only the compare functions remain to be implemented. Although
five accessors can be used, only three compare functions are needed.
The compare functions, being static functions, have access to the
tt(cmpPerson[]) array and to the tt(Listtype selector) variable. This
information is used by the compare functions to call the relevant
access member function
of the two tt(Person) objects, pointed to by the parameters of the
compare functions.
For this, the tt(pointer to member) operator
tt(->*) is used. The element tt(cmpPerson[selector])
contains the function pointers to the functions to be used:
they are the fields
tt(pf), variant tt(cp, i) or tt(d). These fields
return a pointer to a particular access function of a tt(Person)
object.
Through these pointers the functions can be associated to a
particular tt(Person)
object using the pointer to member operator. This results in
expressions like:
center(tt(p1->*cmpPerson[selector].pf.cp))
By this time we have
the name (i.e., address) of an access function for a particular
tt(Person) object. To call this function, parentheses are needed,
one set of parentheses to protect this expression from
desintegrating due to the
high priority of the second set of parentheses, which are
needed for the actual call of the function. Hence, we get:
center(tt((p1->*cmpPerson[selector].pf.cp)()))
Finally, here are the three compare functions:
verb(int Person_dbase::cmpstr(Person const *p1, Person const *p2)
{
return
(
strcmp
(
(p1->*cmpPerson[selector].pf.cp)(),
(p2->*cmpPerson[selector].pf.cp)()
)
);
}
int Person_dbase::cmpint(Person const *p1, Person const *p2)
{
return
(
(p1->*cmpPerson[selector].pf.i)()
-
(p2->*cmpPerson[selector].pf.i)()
);
}
int Person_dbase::cmpdate(Person const *p1, Person const *p2)
{
return
(
(p1->*cmpPerson[selector].pf.d)()
<
(p2->*cmpPerson[selector].pf.d)() ?
-1
:
(p1->*cmpPerson[selector].pf.d)()
>
(p2->*cmpPerson[selector].pf.d)()
);
})
)
|