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

Download Presentation
Download Policy: The content available on the website is offered to you 'AS IS' for your personal information and use only. It cannot be commercialized, licensed, or distributed on other websites without prior consent from the author. To download a presentation, simply click this link. If you encounter any difficulties during the download process, it's possible that the publisher has removed the file from their server.

Recommend


More recommend