Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
To further illustrate a practical use of RTTI, the following
program simulates a trash recycler. Different kinds of trash are inserted
into a single container and then later sorted according to their dynamic types.
//: C08:Trash.h
// Describing trash.
#ifndef TRASH_H
#define TRASH_H
#include <iostream>
class Trash {
float _weight;
public:
Trash(float wt) : _weight(wt) {}
virtual float value() const = 0;
float weight() const { return _weight; }
virtual ~Trash() {
std::cout << "~Trash()" <<
std::endl;
}
};
class Aluminum : public Trash {
static float val;
public:
Aluminum(float wt) : Trash(wt) {}
float value() const { return val; }
static void value(float newval) {
val = newval;
}
};
class Paper : public Trash {
static float val;
public:
Paper(float wt) : Trash(wt) {}
float value() const { return val; }
static void value(float newval) {
val = newval;
}
};
class Glass : public Trash {
static float val;
public:
Glass(float wt) : Trash(wt) {}
float value() const { return val; }
static void value(float newval) {
val = newval;
}
};
#endif // TRASH_H ///:~
The static values representing the price per unit of
the trash types are defined in the implementation file:
//: C08:Trash.cpp {O}
// A Trash Recycler.
#include "Trash.h"
float Aluminum::val = 1.67;
float Paper::val = 0.10;
float Glass::val = 0.23;
///:~
The sumValue( ) template iterates through a
container, displaying and calculating results:
//: C08:Recycle.cpp
//{L} Trash
// A Trash Recycler.
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <typeinfo>
#include <vector>
#include "Trash.h"
#include "../purge.h"
using namespace std;
// Sums up the value of the Trash in a bin:
template<class Container>
void sumValue(Container& bin, ostream& os) {
typename Container::iterator tally = bin.begin();
float val = 0;
while(tally != bin.end()) {
val += (*tally)->weight() *
(*tally)->value();
os << "weight of " <<
typeid(**tally).name()
<< " = " <<
(*tally)->weight() << endl;
++tally;
}
os << "Total value = " << val
<< endl;
}
int main() {
srand(time(0)); // Seed the random number generator
vector<Trash*> bin;
// Fill up the Trash bin:
for(int i = 0; i < 30; i++)
switch(rand() % 3) {
case 0 :
bin.push_back(new Aluminum((rand() %
1000)/10.0));
break;
case 1 :
bin.push_back(new Paper((rand() % 1000)/10.0));
break;
case 2 :
bin.push_back(new Glass((rand() % 1000)/10.0));
break;
}
// Note: bins hold exact type of object, not base
type:
vector<Glass*> glassBin;
vector<Paper*> paperBin;
vector<Aluminum*> alumBin;
vector<Trash*>::iterator sorter = bin.begin();
// Sort the Trash:
while(sorter != bin.end()) {
Aluminum* ap =
dynamic_cast<Aluminum*>(*sorter);
Paper* pp = dynamic_cast<Paper*>(*sorter);
Glass* gp = dynamic_cast<Glass*>(*sorter);
if(ap) alumBin.push_back(ap);
else if(pp) paperBin.push_back(pp);
else if(gp) glassBin.push_back(gp);
++sorter;
}
sumValue(alumBin, cout);
sumValue(paperBin, cout);
sumValue(glassBin, cout);
sumValue(bin, cout);
purge(bin);
} ///:~
The trash
is thrown unclassified into a single bin, so the specific type information is
lost. But later the specific type information must be recovered to properly
sort the trash, and so RTTI is used.
We can improve this solution by using a map that
associates pointers to type_info objects with a vector of Trash
pointers. Since a map requires an ordering predicate, we provide one named TInfoLess
that calls type_info::before( ). As we insert Trash pointers
into the map, they are automatically associated with their type_info
key. Notice that sumValue( ) must be defined differently here:
//: C08:Recycle2.cpp
//{L} Trash
// Recyling with a map.
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <map>
#include <typeinfo>
#include <utility>
#include <vector>
#include "Trash.h"
#include "../purge.h"
using namespace std;
// Comparator for type_info pointers
struct TInfoLess {
bool operator()(const type_info* t1, const type_info*
t2)
const { return t1->before(*t2); }
};
typedef map<const type_info*, vector<Trash*>,
TInfoLess>
TrashMap;
// Sums up the value of the Trash in a bin:
void sumValue(const TrashMap::value_type& p,
ostream& os) {
vector<Trash*>::const_iterator tally =
p.second.begin();
float val = 0;
while(tally != p.second.end()) {
val += (*tally)->weight() *
(*tally)->value();
os << "weight of "
<< p.first->name() //
type_info::name()
<< " = " <<
(*tally)->weight() << endl;
++tally;
}
os << "Total value = " << val
<< endl;
}
int main() {
srand(time(0)); // Seed the random number generator
TrashMap bin;
// Fill up the Trash bin:
for(int i = 0; i < 30; i++) {
Trash* tp;
switch(rand() % 3) {
case 0 :
tp = new Aluminum((rand() % 1000)/10.0);
break;
case 1 :
tp = new Paper((rand() % 1000)/10.0);
break;
case 2 :
tp = new Glass((rand() % 1000)/10.0);
break;
}
bin[&typeid(*tp)].push_back(tp);
}
// Print sorted results
for(TrashMap::iterator p = bin.begin();
p != bin.end(); ++p) {
sumValue(*p, cout);
purge(p->second);
}
} ///:~
We ve modified sumValue( ) to call type_info::name( )
directly, since the type_info object is now available as the first
member of the TrashMap::value_type pair. This avoids the extra call to typeid
to get the name of the type of Trash being processed that was necessary
in the previous version of this program.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |