advanced software engineering with c templates
play

Advanced Software Engineering with C++ Templates Templates II: - PowerPoint PPT Presentation

Advanced Software Engineering with C++ Templates Templates II: Traits Thomas Gschwind <thg at zurich dot ibm dot com> Templates II: Traits Specialization Traits Default Values Th. Gschwind. Advanced Software Engineering with C++


  1. Advanced Software Engineering with C++ Templates Templates II: Traits Thomas Gschwind <thg at zurich dot ibm dot com>

  2. Templates II: Traits § Specialization § Traits § Default Values Th. Gschwind. Advanced Software Engineering with C++ Templates. 182

  3. pvector<T> § Implement the persistent vector data type. § Experiment with the persistent vector and use it in combination with different data types. What do you observe? Why do you observe that behavior? How can it be changed? § What is an interesting data type for T? § Why is it interesting? Th. Gschwind. Advanced Software Engineering with C++ Templates. 183

  4. A Persistent Vector Class (cont’d) template < typename T> What if we use string class pvector { as type parameter? string filename; vector<T> v; void readvector() { Reads a string up to the ifstream ifs(filename); next whitespace for (;;) { T x; ifs >> x; if (!ifs.good()) break; v.push_back(x); } } Writes a string with and without whitespace void writevector() { ofstream ofs(filename); typename vector<T>::iterator fst=v.begin(), lst=v.end(); while (fst!=lst) ofs << *fst++ << endl; } … Th. Gschwind. Advanced Software Engineering with C++ Templates. 184

  5. pvector for string ? § Use template specialization § Partial specialization allows us to change the implementation of a template for a specific class § Very easy to implement but very repetitive template <> class pvector<string> { string filename; vector<string> v; void readvector() { … } void writevector() { … } … // repeat all the other methods as is } Th. Gschwind. Advanced Software Engineering with C++ Templates. 185

  6. Sidebar: Inheritance § The following shows a sample child class inheriting from a sample base class • Typically, we use “: public” to indicate “extends” • Typically, functions to be overridden in the child are declared virtual • We will come back to the gory details (do not use inheritance yet) struct base { virtual void print() const { cout << "base" << endl; } }; struct child : public base { virtual void print() const { cout << "child" << endl; } }; void test( const base &b) { For inheritance to work as “expected” b.print(); we typically use pointers or references. } Th. Gschwind. Advanced Software Engineering with C++ Templates. 186

  7. pvector for string? (cont’d) § Use inheritance • Need to change readvector and writevector in parent class to be virtual • Implied dynamic dispatch although unnecessary • We need a new class with a new name pvectorstring? class pvectorstring : public pvector<string> { virtual void readvector() { … } virtual void writevector() { … } }; • Of course, we can fix the name problem template < typename T> class pvector : public pvector_base<T> { using pvector_base<T>::pvector_base; // inherit constructors }; template <> class pvector<string> : public pvector_base<string> { using pvector_base<string>::pvector_base; // TODO: specialize readvector, writevector Not bad, but we can do better than that }; Th. Gschwind. Advanced Software Engineering with C++ Templates. 187

  8. pvector for string ? (cont’d) § Factor out the persistence logic into a separate interface • Need to pass the vector to be read/written as extra parameter • Yes, not so repetitive • Yes, the persistence logic can be reused template < typename T> struct pvector_serialize { virtual void readvector(string fn, vector<T> &v) = 0; virtual void writevector(string fn, const vector<T> &v) = 0; }; Th. Gschwind. Advanced Software Engineering with C++ Templates. 188

  9. pvector with inheritance based serializer § Pass serializer to pvector in constructor template < typename T> class pvector { string filename; vector<T> v; pvector_serializer<T> *serializer; public : pvector(string fname, pvector_serializer<T> *ser) : filename(fname), serializer(ser) { serializer->readvector(fname, v); } ~pvector() { serializer->writevector(fname, v); } … }; Th. Gschwind. Advanced Software Engineering with C++ Templates. 189

  10. pvector for string ? (cont’d) § Current solution is rather coarse grained § Would be better to just factor out the read and write function § Gives better reuse (left as exercise) template < typename T> struct element_serializer { virtual void read(ifstream &i, T &elem) { … }; virtual void write(ofstream &o, const T &elem) { … }; }; § Trouble is we always use virtual method calls although • We know the type of T • Moreover, we typically know the serializer to be used upfront Th. Gschwind. Advanced Software Engineering with C++ Templates. 190

  11. Templates: Trait Classes § Templates can also be used to provide polymorphism • Same functionality, same everything, just more efficient § Inheritance allows to define an interface to be provided by subtypes § Templates allow us to define an interface to be used (traits) Default template < typename T> struct persistence_traits { implementation static void read(ifstream &i, T &elem) {…} static void write(ofstream &o, const T &elem) {…} } § With inheritance subtypes may override members § The traits class is specialized for different types (override members) Same principle as template <> struct persistence_traits<string> { for min<char*> static void read(ifstream &i, string &elem) {…} static void write(ofstream &o, const string &elem) {…} } Th. Gschwind. Advanced Software Engineering with C++ Templates. 191

  12. pvector : Refactoring § First, let’s refactor the pvector class to move the read and write methods into external classes… Th. Gschwind. Advanced Software Engineering with C++ Templates. 192

  13. pvector : Using the Trait class template < typename T> struct persistence_traits { static void read(ifstream &i, T &elem) {…} static void write(ofstream &o, const T &elem) {…} } template <> struct persistence_traits<string> { static void read(ifstream &i, string &elem) {…} static void write(ofstream &o, const string &elem) {…} } template < typename T> Since T is known at compile- class pvector { time, this function is known at void writevector() { compile-time ofstream ofs(filename); vector<T>::iterator fst=v.begin(), lst=v.end(); while (fst!=lst) persistence_traits<T>::write(ofs, *fst++) ; } Th. Gschwind. Advanced Software Engineering with C++ Templates. 193

  14. pvector for string ? (cont’d) § Factor out the persistence logic into a separate class • Yes, not so repetitive • Yes, the persistence logic can be reused • Yes, everything determined at compile time § But what if we want to use different persisters? • Now, let’s use our traits class in a polymorphic way • Sort of like we are used from object-oriented programming • Except that types are resolved during compile time Th. Gschwind. Advanced Software Engineering with C++ Templates. 194

  15. Traits are similar to Inheritance except … § Inheritance allows us to use different implementations § Similarly, allow to specify the use of different implementations, independently of the trait’s type § … except all type inference is known, verified, and resolved during compile time template < typename T , typename P > class pvector { void writevector() { ofstream ofs(filename); vector<T>::iterator fst=v.begin(), lst=v.end(); while (fst!=lst) P::write(ofs, *fst++) ; } } pvector<string , persistence_traits<string> > avector; Th. Gschwind. Advanced Software Engineering with C++ Templates. 195

  16. pvector for string ? (cont’d) § Make the persistence class generic • Yes, now we can even specify the persister • Yes, the code is as readable as generic Java code • Yes, BUT creating a new object becomes tedious Th. Gschwind. Advanced Software Engineering with C++ Templates. 196

  17. Templates: Default Values § Always having to specify the persistence trait is tedious § Like with function parameters, C++ allows to assign a default value to template parameters § Now, we can only complain about certain style issues template < typename T, typename P =persistence_traits<T> > template < typename T, typename P > class pvector { Default traits class void writevector() { ofstream ofs(filename); vector<T>::iterator fst=v.begin(), lst=v.end(); while (fst!=lst) P::write(ofs,*fst++); } } pvector<string, persistence_traits<string> > avector; ============================= Th. Gschwind. Advanced Software Engineering with C++ Templates. 197

  18. pvector: Beauty Contest § Typically, as part of a parameterized class, we include certain typedefs for convenience and readability § We will come back to the usefulness when we talk about binders template < typename T, typename P=persistence_traits<T> > class pvector { … public : typedef P persister; typedef typename vector<T>::iterator iterator; … As discussed, typename helps the compiler to identify that the void writevector() { following is a typename ofstream ofs(filename); iterator fst=v.begin(), lst=v.end(); while (fst!=lst) persister ::write(ofs,*fst++); } } Th. Gschwind. Advanced Software Engineering with C++ Templates. 198

Recommend


More recommend