Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
If you inspect the char_traits specialization for wchar_t,
you ll see that it is practically identical to its char counterpart:
template<> struct char_traits<wchar_t> {
typedef wchar_t char_type;
typedef wint_t int_type;
typedef streamoff off_type;
typedef wstreampos pos_type;
typedef mbstate_t state_type;
static void assign(char_type& c1, const
char_type& c2);
static bool eq(const char_type& c1, const
char_type& c2);
static bool lt(const char_type& c1, const
char_type& c2);
static int compare(const char_type* s1,
const char_type* s2, size_t n);
static size_t length(const char_type* s);
static const char_type* find(const char_type* s,
size_t n,
const char_type& a);
static char_type* move(char_type* s1,
const char_type* s2, size_t
n);
static char_type* copy(char_type* s1,
const char_type* s2, size_t
n);
static char_type* assign(char_type* s, size_t n,
char_type a);
static int_type not_eof(const int_type& c);
static char_type to_char_type(const int_type& c);
static int_type to_int_type(const char_type& c);
static bool eq_int_type(const int_type& c1,
const int_type& c2);
static int_type eof();
};
The only real difference between the two versions is the set
of types involved (char and int vs. wchar_t and wint_t).
The functionality provided is the same. This
highlights the fact that traits classes are indeed for traits, and the
things that change between related traits classes are usually types and
constant values, or fixed algorithms that use type-related template parameters.
Traits classes tend to be templates themselves, since the types and constants
they contain are seen as characteristics of the primary template parameter(s) (for
example, char and wchar_t).
It is also useful to be able to associate functionality
with template arguments, so that client programmers can easily customize
behavior when they code. The following version of the BearCorner program,
for instance, supports different types of entertainment:
//: C05:BearCorner2.cpp
// Illustrates policy classes.
#include <iostream>
#include "BearCorner.h"
using namespace std;
// Policy classes (require a static doAction()
function):
class Feed {
public:
static const char* doAction() { return
"Feeding"; }
};
class Stuff {
public:
static const char* doAction() { return
"Stuffing"; }
};
// The Guest template (uses a policy and a traits
class)
template<class Guest, class Action,
class traits = GuestTraits<Guest> >
class BearCorner {
Guest theGuest;
typedef typename traits::beverage_type beverage_type;
typedef typename traits::snack_type snack_type;
beverage_type bev;
snack_type snack;
public:
BearCorner(const Guest& g) : theGuest(g) {}
void entertain() {
cout << Action::doAction() << "
" << theGuest
<< " with " << bev
<< " and " << snack
<< endl;
}
};
int main() {
Boy cr;
BearCorner<Boy, Feed> pc1(cr);
pc1.entertain();
Bear pb;
BearCorner<Bear, Stuff> pc2(pb);
pc2.entertain();
} ///:~
The Action template parameter in the BearCorner
class expects to have a static member function named doAction( ),
which is used in BearCorner<>::entertain( ). Users can choose
Feed or Stuff at will, both of which provide the required
function. Classes that encapsulate functionality in this way are referred to as
policy classes. The entertainment policies are provided above through Feed::doAction( )
and Stuff::doAction( ). These policy classes happen to be ordinary
classes, but they can be templates, and can be combined with inheritance to
great advantage. For more in-depth information on policy-based design, see
Andrei Alexandrescu s book, the
definitive source on the subject.
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |