c++ (7. vorlesung) exercise discussion, advanced templates · what if we have multiple source files...
Post on 08-May-2020
4 Views
Preview:
TRANSCRIPT
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation1
C++ (7. Vorlesung)Exercise Discussion, Advanced Templates
Thomas Gschwind<thg@....ibm....>
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation2
Agenda
� Exercise Discussions– RPN Calculator
• Complex numbers (Operators)• min (Complex)
– Connect 4 – finding whether the opponent can win– pvector with strings
� Advanced Template Topics– Template Implementation: C++ vs. Java vs. C#– Traits– Dynamic Static Algorithm Selection
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation3
Complex numbers
� The standard complex class is trivial– Don’t use free-standing operators in combination with friends– Make the operators members of the class– Input and output is the only exception
� RPN calculator with complex– Many colleagues did not implement it– Many colleagues used the C++11 complex class (my mistake)– Hint, you need to ensure that you have all the operators used by the RPN
calculator: >>, <<, ==, !=, <, …
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation4
RPN<complex> and min
� Yes, there is no ordering defined on complex classes
� Solutions I have seen/heard:– Define min for complex as a<b iff |a| < |b|
• Creative solution but not really nice
– min function part of the RPN calculator => bad choice• In C++ we don’t have to cram random stuff into random classes• Specialization for complex requires rewriting of RPN calculator
– Adapt min to return a pointer to the minimum, overload for complex and return NULL
• Creative solution but makes min not really nice to use
– Overload min for complex and throw an exception• My favorite solution
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
pvector<string>
� Many colleagues tried the pvector with strings and did not observe anything– “Hello”, “World”, “Somestring”, “Apple”, “Banana”:
are not really useful strings to test something– “This is interesting”, “Tab\tstop”, “And\nnewlines?”, “Umlaute? ÄÖÜäöü”:
are better strings
� Then “we” would realize that white spaces are problematic with our pvector implementation, especially “\n” should have been obvious…
� So, how do we solve this? This is the matter of today’s lecture.
5
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation6
Connect 4: Checking if the other player could win
� A minority of colleagues look for– 3 stones in a row (buggy x_xx) or– a pattern of 3 in a series of 4 fields (better but clumsy)
� Most colleagues drop a stone and use the has_won method
� You don’t need to copy the playfield every time, just remove the stone after the test
� Do this for every column – dead simple
� Yes, with this implementation, you can already recursively start computing the next possible move for the opponent, etc.
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation7
Connect 4: Interoperability
� Exercises– Get an implementation from your colleague – almost nobody did this– Do it for the next exercise when we do connect 4 with inheritance
� A few problems you would have encountered (or will encounter)– You may want to extend your playfield– The playfield your computer player receives, might be different if running
in a colleagues implementation– Just use the provided methods, not more– If you rely on your own playfield, copy whatever you get into your own
playfield that provides all the extra methods – problem solved ☺– Maybe you do not want to extend your main playfield but use your own
playfield class inside the computer player anyway
� The above are just some ideas to get you thinking!
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation8
Agenda
� Exercise Discussions– RPN Calculator
• Complex numbers (Operators)• min (Complex)
– Connect 4 – finding whether the opponent can win– pvector with strings
� Advanced Template Topics– Template Implementation: C++ vs. Java vs. C#– Traits– Dynamic Static Algorithm Selection
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
C++ Templates – Implementation
� C++ creates a new artifact for each new use of a template(almost, modern C++ compilers do some optimization)
� Similar to advanced C macros
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
template<typename T>inline T min(T a, T b) {
return a<b?a:b;}const double pi=3.141596;void f() { // create implementation for
min(2.718282,1.0); // min<double>min('a','z'); // min<char>min(1,26); // min<int>min(pi,2.718282); // … already createdmin<char>('a',26); // … already createdmin(2.718282,1.0); // … already created
}
C++ Templates – Implementation (cont’d)
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
C++ Templates – Consequences
� C++ creates a new artifact for each new use of a template– Uses “more” memory– Templates need to be defined in the header file
(Otherwise, the compiler cannot instantiate them when they are used)– Can be used in combination with built-in types– Can be used in combination with non-typenames
(e.g., int s, char s, …)
– Better optimization since the template can be optimized for each specific template argument
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
C++ Templates – Question
� What if we have multiple source files instantiating the same template?– file1.cc instantiating vector<int>
file2.cc instantiating vector<int>– Will the code for vector<int> be generated twice once for each object file?– Will the executables be double the size when main.cc uses both of them?
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
C++ Templates – Question
� What if we have multiple source files instantiating the same template?– file1.cc instantiating vector<int>
file2.cc instantiating vector<int>– Will the code for vector<int> be generated twice once for each object file?
Yes!– Will the executables be double the size when main.cc uses both of them?
No!
– Why? The corresponding code of the templates is put into a COMDAT section of the object file. The linker only uses the first such section with the same name.
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
Java Generics – Implementation
� Java Generics are implemented using type erasure– All the template parameters are internally replaced with their type bound
(Object by default)– The compiler inserts casts as necessary
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
Java Generics – Implementation
public class Iterator<E> {E next();boolean hasNext();
}
Iterator<String> iter=…;while(iter.hasNext()) {
String s=iter.next();…
}
public class Iterator {Object next();boolean hasNext();
}
Iterator iter=…;while(iter.hasNext()) {
String s=(String)iter.next();
…}
Source Code “Generated” Code
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
Java Generics – Consequences
� Java keeps only one copy of the class– Java Generics “cannot” be used with primitive types– Need to be encapsulated in the corresponding wrapper class– Java does this since quite some time automatic BUT
generating wrappers requires memory, stresses the garbage collector
� Allows forward and backward compatibility with existing code
public String oops(Integer x) {List<String> ys = new LinkedList<String>();List xs = ys;xs.add(x); // compile-time unchecked warni ngreturn ys.iterator().next(); } // run-time err or
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
Java Generics – Consequences (cont’d)
� Java keeps only one copy of the class– Uses “more” memory – many casts need to be inserted– Programs can be unsafe although they look safe
(but the compiler will warn about it)– Allows forward and backward compatibility with existing code– Cannot create elements of E
• No new E or new E[]
• Especially the latter can be annoying, need to use Object[] and a lot of (E)casts which are unsafe
– JIT cannot optimize for different parameterizations
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
C# Generics – Implementation
� C# thought about Generics from the start– Unlike the Java VM, the CLR is already generic– Generics can be parameterized with primitive types– CLR JIT can optimize code for different parameterizations
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
C# Generics – Consequences
� Templates are stored as such in the byte-code� Can be used for built-in types� Can use arrays of type T.� Cannot be used in combination with non-typenames
(e.g., int s, char s, …)
� Better optimization since the template can be optimized for each specific template argument
� Uses “more” memory� Is somewhere between C++ and Java
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation20
Agenda
� Exercise Discussions– RPN Calculator
• Complex numbers (Operators)• min (Complex)
– Connect 4 – finding whether the opponent can win– pvector with strings
� Advanced Template Topics– Template Implementation: C++ vs. Java vs. C#– Traits– Dynamic Static Algorithm Selection
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation21
pvector<string>
� 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 happens if we pass the pvector around?
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation22
A Persistent pvector Class (cont’d)
template<typename T>class pvector {
string filename;vector<T> v;void readvector() {
ifstream ifs(filename);for(;;) {
T x; ifs >> x; if(!ifs.good()) break;v.push_back(x);
} }void writevector() {
ofstream ofs(filename);vector<T>::iterator fst=v.begin(), lst=v.end();while(fst!=lst) ofs << *fst++ << endl; }
…
pvector.cc
What if we use string as type parameter?
Reads a string up to the next whitespace
Writes a string with and without whitespace
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation23
pvector for string?
� Use partial 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
pvector.cc
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation24
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 : pvector<string> {virtual void readvector() { … }virtual void writevector() { … }
}
pvectorstring.cc
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation25
pvector for string? (cont’d)
� Factor out the persistence logic into a separate interface– Yes, not so repetitive– Yes, the persistence logic can be reused
template<typename T>struct pvector_serialize {virtual void readvector(string fn) = 0;virtual void writevector(string fn) = 0;
}
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
pvector with inheritance based serializer
� Pass serializer to pvector in constructor
26
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();
}
~pvector() { serializer->writevector(); }
…
};
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation27
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)
� Trouble is we always use virtual method calls although – We know the type T– And typically the serializer to be used upfront
� Templates, if used correctly allow the same mechanism as we just implemented with inheritance– Same functionality, same everything, just more efficient
template<typename T> struct element_serializer {virtual void read(ifstream &i, T &elem) = 0;virtual void write(ofstream &o, const T &elem) = 0;
}
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation28
Traits are similar to Inheritance but Different� Inheritance allows to define an interface to be provided by subtypes
� Traits define an interface to be used for any type ANDprovide a default implementation as well
template<typename T>struct persister {static void read(ifstream &i, T &elem) {…}static void write(ofstream &o, const T &elem) {…} }p
vector.cc
Default implementation
� With inheritance subtypes may override methods
� With traits, specializations may override methods
template<>struct persister<string> {static void read(ifstream &i, T &elem) {…}static void write(ofstream &o, const T &elem) {…} }p
vector.cc
Same principle as for min<string>
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
Pvector Refactoring
� First, let’s refactor the pvector class to move the read and write methods into external classes…
29
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation30
pvector for string? (cont’d)
template<typename T>struct persister {static void read(ifstream &i, T &elem) {…}static void write(ofstream &o, const T &elem) {…} }
template<>struct persister<string> {static void read(ifstream &i, T &elem) {…}static void write(ofstream &o, const T &elem) {…} }
template<typename T>class pvector {void writevector() {
ofstream ofs(filename);vector ::iterator fst=v.begin(), lst=v.end();while(fst!=lst) persister ::write(ofs,*fst++); }
pvector.cc
Depending on the type of container in use — can be determined by the compiler
<T><T>
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation31
pvector for string? (cont’d)
� Factor out the persistence logic in 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
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation32
Traits are similar to Inheritance but Different
� Inheritance allows us to use different implementations� Similarly, allow to specify the use of different implementations,
independently of the trait’s type� The key difference is that 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<T>::write(ofs,*fst++); }
pvector.cc
pvector<string, persister<string> > myvector;
foo.cc
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation33
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
Are we picky?!?
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation34
pvector for string? (cont’d)
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<T>::write(*fst++); }
pvector.cc
template<typename T, typename P=persister<T> >class pvector {
…void writevector() {
ofstream ofs(filename);vector<T>::iterator fst=v.begin(), lst=v.end();while(fst!=lst) P::write(*fst++); }
pvector.cc
This is not the most pretty implementation
Typically called a traits class
pvector<string> myvector;
foo.cc
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation35
pvector for string Beauty Contest
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<T>::write(*fst++); }
pvector.cc
template<typename T, typename P=persister<T> >class pvector {typedef P persister;typedef typename vector<T>::iterator iterator;…void writevector() {
ofstream ofs(filename);iterator fst=v.begin(), lst=v.end();while(fst!=lst) persister::write(*fst++); }
pvector.cc
This helps the compiler to identify that the following is a typename
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation36
Agenda
� Advanced Uses of Templates– Traits– Not just typenames?
� C++ Standard Library II– Iterators & Algorithms– Binders
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation37
Different Types of Iterators
Iteration ++ ++ ++ ++ --
input forwardbi-
directional
Abbrev.
Access
Read
Write
In For Bi
-> -> ->
=*p =*p =*p
*p= *p=
output
Out
*p=
Categoryrandom-access
Ran
=*p
-> []
*p=
++ + +=-- - -=
Compare == != == != == !=== < <=!= > >=
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation38
Types of Iterators (cont‘d)
Input Output
Forward
Bidirectional
Random Access
struct input_iterator_tag {};struct output_iterator_tag {};struct forward_iterator_tag :
public input_iterator_tag {};struct bidirectional_iterator_tag :
public forward_iterator_tag {};struct random_access_iterator_tag :
public bidirectional_iterator_tag {};
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation39
Iterator Tags?
� Algorithm selection
� Saves typing overhead
� Simulation of bound genericity
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation40
Why Iterator Tags? (cont‘d)
template <class In> iterator_traits<In>::difference_type__distance(In first,In last, input_iterator_tag dum my) {
iterator_traits<In>::difference_type n = 0;while(first!=last) { ++first; ++n; }return n;
}template <class Ran> iterator_traits<Ran>::difference_type__distance(Ran first,Ran last, random_access_iterat or_tag dummy) {return last-first;
}template <class I> inlineiterator_traits<I>::difference_type distance(I firs t,I last) {typedef typename iterator_traits<I>::iterator_category cat;return __distance(first,last, cat());
}
iterator
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation41
iterator_traits
template <class Iter> struct iterator_traits< Iter> {typedef typename Iter::iterator_category iterator_category;typedef typename Iter::value_type value_type;typedef typename Iter::difference_type difference_type;typedef typename Iter::pointer pointer;typedef typename Iter::reference reference;
};template <class T> struct iterator_traits<T*> {typedef random_access_iterator_tag iterator_category;typedef T value_type;typedef ptrdiff_t difference_type;typedef T* pointer;typedef T& reference;
};
iterator
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation42
„Advise“
Decide which algorithms you want; parameterize them so that they work for a
variety of suitable types and data structures.Bjarne Stroustrup
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation43
Summary
� Exercise Discussions– RPN Calculator
• Complex numbers (Operators)• min (Complex)
– Connect 4 – finding whether the opponent can win– pvector with strings
� Advanced Template Topics– Template Implementation: C++ vs. Java vs. C#– Traits– Dynamic Static Algorithm Selection
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation44
Exercise 1
� Adapt your persistent pvector to make use of the new persister trait. Also implement a pset that implements a persistent set (based on std::set).
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation45
Exercise 2
� Extend your dictionary programInstead of printing out all unknown words, your program should allow the user to correct them or insert them into the dictionary if spelled correctly but not in the dictionary. Make use of your persistent set implementation form exercise 1.
Please make a copy of the old version of the dictionary program.
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation46
Exercise 3
� Implement a template based version of the Connect 4 game where players. Connect 4 builds on a playing field composed out of 7 columns each having 6 rows. When a player puts a stone into a column, gravity pulls the stone towards the lowest unoccupied column. The player who first has 4 stones in a row (horizontally, vertically, diagonally) wins.
� After each turn display the game field using simple ASCII graphics. Implement the game in such a way that players can be exchanged easily using templates. The precise interfaces to follow will be published at the lecture’s homepage. It is important that you follow these interfaces religiously.
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation47
Exercise 4
� Implement a computer player. Again, the computer player of this version does not have to be intelligent. At a minimum, however, the computer player should be able to identify whether he can win the game by placing a stone. Let your computer player compete against computer players from your colleagues.
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation
Exercise 5: rpn_calculator<complex>
� Make sure the RPN calculator works with both– with your implementation of complex numbers– With the C++11 version of complex numbers
48
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation49
Next Lecture
� Algorithms, Binders, and Programming with Templates
Have fun solving the examples!
See you next week!
IBM Research – ZurichBusiness Integration Technologies
© 2010 IBM Corporation50
Agenda
� Exercise Discussions– RPN Calculator
• Complex numbers (Operators)• min (Complex)
– Connect 4 – finding whether the opponent can win– pvector with strings
� Advanced Template Topics– Template Implementation: C++ vs. Java vs. C#– Traits– Dynamic Static Algorithm Selection
top related