CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits A General Look at Type...
-
Upload
benedict-tucker -
Category
Documents
-
view
213 -
download
1
Transcript of CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits A General Look at Type...
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
A General Look at Type Programming in C++
• Associated types (the idea)– Let you associate a type with another type– Given a type parameter, let you get the other type– Let you control encapsulation of type information
• Typedefs + specialization (the mechanisms)– Let you give a known type another name– Let you name associated types consistently
• Across user-defined/built-in const/non-const types
• Traits (the technique)– Let you associate user-defined and built-in types– Let you provide consistent access across types
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
Basic Example: Associated Types (the Idea)• Want to swap the values of two
locations in memory• Basic idea is simple
– Declare a temporary variable– “Remember” value from location i1 aliases in temp
– Copy value from location i2 aliases into location i1 aliases
– Copy “remembered” value from temp into location i2 aliases
• But, code to left won’t compile– How can we declare temp’s type?– How can we get T from I?
// Based on Austern pp. 34
template <typename I>
void swap (I i1, I i2)
{
T temp = *i1;
*i1 = *i2;
*i2 = temp;
}
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
Typedef + Specialization (the Mechanisms)
• Use specialization of a general traits struct template– Still have to parameterize struct iterator_traits with typename I
– Can’t do full specialization since we’re using type parameter T
– Gives us different versions of the iterator_traits struct
• For user-defined, built-in types• For const, non-const types
– Remember that the C++ compiler will select the most specific match
// Based on Austern, pp. 35
template <typename I>
struct iterator_traits {
typedef typename I::value_type
value_type;
};
template <typename T>
struct iterator_traits<T*> {
typedef T value_type;
};
template <typename T>
struct iterator_traits<const T*>
{
typedef T value_type;
};
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
Traits (the Technique)
• Given the iterator type I • Traits provide an interface to I‘s associated types
– E.g., iterator_traits<I>::value_type– Let us obtain and use those types in template code
• In general, let us go from containers to iterators to traits to associated types– Even within templates
template <typename I>
void swap (I i1, I i2)
{
iterator_traits<I>::value_type
temp = *i1;
*i1 = *i2;
*i2 = temp;
}
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
Another Type Programming Example
• Goal– Print the contents of
different data sets• First approach
– Use list and vector • data stored in ranges
– Iterator for ostream • lets us print to cout
– The copy algorithm• Copies data ranges
– Get similar code 2 places• Desired improvement
– Factor out common code– How can we make this
code more generic?– What type info do we need
to do that?
#include <iostream> // For cout, manipulators.#include <list> // For lists.#include <vector> // For vectors.#include <iterator> // For ostream iterator.using namespace std;
int main (int, char *[]) {
list<char> l; l.push_back ('a'); l.push_back ('b'); l.push_back ('d'); cout << "list l contains: "; ostream_iterator<char> osic (cout, " "); copy (l.begin(), l.end(), osic); cout << endl;
vector<int> v; v.push_back (1); v.push_back (2); v.push_back (4); cout << "vector v contains: "; ostream_iterator<int> osii (cout, " "); copy (v.begin(), v.end(), osii); cout << endl;
return 0;}
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
Containers Iterators Traits Types
• Use of copy algorithm– Takes three iterators
• Two input iterators• One ostream iterator
• Input iterator types– Can we get those from the
container?– If so, how?
• Output iterator type– Parameterized with char
vs. int– Can we get that
parameterized type from the input iterators?
– If so, how?
#include <iostream> // For cout, manipulators.#include <list> // For lists.#include <vector> // For vectors.#include <iterator> // For ostream iterator.using namespace std;
int main (int, char *[]) {
list<char> l; l.push_back ('a'); l.push_back ('b'); l.push_back ('d'); cout << "list l contains: "; ostream_iterator<char> osic (cout, " "); copy (l.begin(), l.end(), osic); cout << endl;
vector<int> v; v.push_back (1); v.push_back (2); v.push_back (4); cout << "vector v contains: "; ostream_iterator<int> osii (cout, " "); copy (v.begin(), v.end(), osii); cout << endl;
return 0;}
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
template <class T, size_t N>
struct block{
typedef T value_type;
typedef value_type * pointer;
typedef value_type & reference;
typedef const_value_type* const_pointer;
typedef const value_type & const_reference;
typedef size_t size_type;
……
typedef pointer iterator;
typedef const_pointer const_iterator;
……
};
From Austern, pp. 61
Containers Iterators (Types)
Every STL container must provide these types
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
template <class T, size_t N>
struct block{
……
iterator begin() {return data;}
iterator end() {return data+N;}
const_iterator begin() const {return data;}
const_iterator end() const {return data+N;}
……
T data [N];
};
Containers Iterators (Instances)
From Austern, pp. 61
Every STL container must produce iteratorsof the correct type (Factory Method Pattern)
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
Containers Iterators Traits Types• Can now write a
(somewhat) generic print_container function template– Takes container type T as
a template type parameter– Takes const reference to
container t as a function parameter
• Uses associated types– Obtained from passed
container types– Uses typename where
necessary• Uses iterator accessors
– t.begin() and t.end()
#include <iterator>#include <algorithm>
using namespace std;
template <typename T>void print_container (const T & t) {
ostream_iterator <typename T::value_type> osi (cout, " ");
copy (t.begin(), t.end(), osi);
cout << endl;}
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
How Making a Function Generic Can Help• Program is now simpler
– Manipulates data, containers– Leaves printing details to the
generic print_container function template
• However, there is still one remaining limitation– Cannot use for built-in arrays,
or variables like double, or char[]
– We will fix this limitation next• By changing to a range-
based print function interface• By declaring our own traits
#include "print_container_T.h"#include <iostream> // For cout.#include <list> // For lists.#include <vector> // For vectors.using namespace std;
int main (int, char *[]) {
list<char> l; l.push_back ('a'); l.push_back ('b'); l.push_back ('d'); cout << "list l contains: "; print_container (l);
vector<int> v; v.push_back (1); v.push_back (2); v.push_back (4); cout << "vector v contains: "; print_container (v);
return 0;}
/* output is
list l contains: a b d
vector v contains: 1 2 4 */
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
Declaring Your Own Traits• Could have used
pointer traits already provided by the STL– But, it’s good to see the
technique itself again• So you can associate
other types as needed– Notice use of partial
specialization and use of typename, again
– Notice separate partial specializations for const and non-const, again
• Now we have our associated types– Can use in an even
more generic print function template
template <typename T>struct print_traits { typedef typename T::value_type value_type;};
// partial specialization for pointerstemplate <typename T> struct print_traits<T *> { typedef T value_type;};
// partial specialization for const pointerstemplate <typename T> struct print_traits<const T *> { typedef T value_type;};
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
Using Your Traits in a Generic Function Template
• Generic print function template– Takes iterator type parameter– Takes iterators as function
parameters (define a range)• Used typedef for convenience
– Here, as a kind of local type name declaration
– Avoid excessive use, but can aid coding style in some cases
• Applies generically to iterators– Including const and non-const
pointers to memory locations
#include "print_T.h"#include <iterator>#include <algorithm>
using namespace std;
template <typename I>void print (I i, I j) {
typedef typename print_traits<I>::value_type VTYPE;
ostream_iterator<VTYPE> osi (cout, " ");
copy (i, j, osi); cout << endl;}
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
How Generic Type Programming Has Helped
• Program is as simple as it was before
• But now has added capabilities– Can print variables– Can print arrays
• All using generic type programming in C++
#include "print_T.h"#include <iostream> // For cout, manipulators.#include <list> // For lists.#include <vector> // For vectors.#include <cstring> // For strlen.using namespace std;int main (int, char *[]) {
list<char> l; l.push_back ('a'); l.push_back ('b'); l.push_back ('d'); cout << "list l contains: "; print (l.begin(), l.end());
vector<int> v; v.push_back (1); v.push_back (2); v.push_back (4); cout << "vector v contains: "; print (v.begin(), v.end ());
char * s = "hello, world!"; cout << "C-style string s contains: " << endl; print (s, s + strlen(s));
const double d = 3.141; cout << "Const double precision floating \n" " point number d contains: "; print (&d, (&d)+1); return 0;}
/* output is
list l contains: a b d
vector v contains: 1 2 4
C-style string s contains:
h e l l o , w o r l d !
Const double precision floating
point number d contains: 3.141 */
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits
Concluding Remarks• Associated types are a powerful idea in C++
– Let you use templates more effectively, easily– Let you make programs simpler, better encapsulated
• Typedefs give a way to provide consistent type names– Let user-defined types declare associated types– Let you create type names for other purposes, convenience
• Traits abstract away problems with associating types– Across user-defined, built-in, const, non-const types– Ensures consistency, makes types/algorithms more generic
• The STL takes these ideas even further– Allows algorithms to be bound to specific iterator categories– For arbitrary reasons, like performance/correctness trade-off