IBM Research – Zurich Business Integration Technologies C++ (10. Vorlesung) C++11, Factories, Multi Methods Thomas Gschwind <thg@zurich....> © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Agenda C++11 Factories (Virtual Constructors) – Factories – Clone – Exemplars Multi methods 2 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Introduction History – Last change to C++ was about 10 years ago (C++03) – Many features already included in several compilers – Final specification in 2011 Does not relieve the engineer Difficult to change language from choosing good algorithms! – Compatibility – new keywords could clash with old code – Goal to make it easier for “newbies” and better for experts Keep the original design goals – – – – Generate programs of high performance Versatile Don’t force a specific paradigm Keep zero overhead principle – A feature that is unused must not cost anything Language and libraries are both part of the standard – Java was the “first” language to include the library as part of the standard 3 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Language Changes The nullptr The auto type Constant Expressions Range for Lambda functions Initializer lists Constructors 4 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies nullptr New way for saying NULL Frequently defined as 0 => problems with ints 5 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Range For (foreach) Operator Copied over from Java (which copied it over from C# (which …)) Easier to use than C++’s standard for function Useful in combination with initializer lists void print(const vector<int> &v) { for(const auto &i : v) { cout << i << endl; } } 10 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Initializer Lists Want to initialize a vector with a set of elements? Don’t like to use a series of push_back calls? => Initializer lists are for you Represents a list of values New constructor invocation Standard library containers modified to support this #include<initializer_list> class MyVec { int *a; public: MyVec(initializer_list<int> seq) : a(new int[seq.size()]) { copy(seq.begin(), seq.end(), a); }; MyVec squares{0,1,4,9,16}; 12 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Constructors Constructors may invoke other constructors – For instance, for our own String class, we could provide String() : String(NULL) {} Constructors may be derived – For instance, in the subclass to derive all constructors from Base, include using Base::Base; Copy constructor may be explicitly removed (same for the assignment operator) – Simply declare as pvector(const pvector&) = delete; pvector& operator=(const pvector&) = delete; 13 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Calling Constructors C++11 allows to call peer constructors Similar to Java Can make code more readable class SomeType int number; { public: SomeType(int new_number) : number(new_number) {} SomeType() : SomeType(42) {} }; 14 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Standard Library Changes Hash tables (unordered_map/set) Threads unique_ptr, shared_ptr, weak_ptr Regular Expressions 15 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Threads (Starting and Joining) So far, C++ developers had to rely on – BOOST thread library, POSIX threads, Other non-standard solutions C++ provides a thread class – Needs to be passed to a functor (function, function object, lambda function) – The new thread is started immediately – Threads may be joined with the join() member function #include<thread> struct printchar { char c; printchar(char ch) : ch(c) {} void operator()() { for(int i=0; i<9999; ++i) cout << c; } } int main(int argc, char *argv[]) { thread t1(printchar(‘a’)), t2(printchar(‘b’)); t1.join(); t2.join(); } 17 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Threads (Mutexes) Similar to the Java synchronized(object) { … } mechanism C++ provides mutexes However, instead of changing the language, it is implemented as library (but almost invisible to developers) #include<thread> mutex mtx; int cnt=0; struct printchar { char c; printchar(char ch) : ch(c) {} void operator()() { for(int i=0; i<9999; ++i) { { lock_guard lock(mtx); ++cnt; } cout << c; } } } 18 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Threads (Async Executions, Futures) Asynchronous execution is provided by the async function Async is clever enough to know the platform and only executes the function in a separate thread if beneficial on the platform template <typename I> void parsum(I begin, I end) { int sum=0; int sz=end-begin; if(sz<1000) { while(begin!=end) sum+=*begin++; } else { mid=begin+sz/2; auto handle=async([=]{ return parsum(begin,mid); }); sum+=parsum(mid, end); sum+=handle.get(); } return sum; } 19 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Agenda C++11 Factories (Virtual Constructors) – Factories – Clone – Exemplars Multi methods 22 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies The Number Example Number We have a hierarchy of number types inherits Want to calculate with Complex Integer Fraction these types (re,im) i (c:d) Types are not known during compile time => operation to be invoked must be determined during run time void solve_p2(Number p, Number q, Number s[2]) { Number t=sqrt(p*p*fraction(1,4)-q); s[0]=p*fraction(1,2)-t; s[1]=p*fraction(1,2)+t; } 23 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Only use complex? Exclusively use the complex data type C = R × R, R = Q ∪ I, Q = Z × Z Nice idea BUT – double ≠ R – Is there a number type in C++ that is a superset of all other number types? Is it possible to implement such a type? – Efficiency 24 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Virtual Methods We have different data types: complex, fraction, … Problem: – operator+/-/*/… should not be virtual – And even if you don‘t care the code will really look terrible Hence, we implement our own methods: add/sub/mul/… – The real method to invoke (complex * complex or complex * fraction, etc.) will be determined during run time – Problem, only works with pointers number* p2(number *x, number *p, number *q) { return x->mul(x)->add(x->mul(p))->add(q); } 25 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Virtual Methods We have different data types: complex, fraction, … Problem: Beides durch – operator+/-/*/… should not be virtual – And even if you don‘t care the code will einen really Wrapper look terrible lösbar! Hence, we implement our own methods: add/sub/mul/… – The real method to invoke (complex * complex or complex * fraction, etc.) Memory will be determined during run time leak?!? – Problem, only works with pointers Unbequem!!! number* p2(number *x, number *p, number *q) { return x->mul(x)->add(x->mul(p))->add(q); } 26 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Numbers: The Wrapper class Number { protected: number *n; int *ctr; public: Number(number *nbr): n(nbr), ctr(new int(1)) {} Number(const Number &nbr):n(nbr.n),ctr(nbr.ctr) { ++*ctr; } ~Number() { if(--*ctr==0) { delete n; delete ctr; } } Number &operator=(const Number &b) { if(--*ctr==0) { delete n; delete ctr; } n=b.n; ctr=b.ctr; ++*ctr; return *this; } // … 27 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Implementation with Virtual Methods class fraction: public number { public: fraction(int cntr=0, int denom=1) : c(cntr), d(denom), os(NULL) {} virtual Number add(Number n2) const { complex *c2; fraction *f2; if((f2=dynamic_cast<fraction*>(n2.n))!=NULL) { return new fraction((*this)+(*f2)); } else if ((c2=dynamic_cast<complex*>(n2.n))!=NULL) { complex c1(static_cast<double>(*this)); return new complex(c1+(*c2)); } else { throw ...; } } // … }; 28 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Virtual Methods? (cont‘d) Real Problems – New data type – New Operation 29 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Agenda C++11 Factories (Virtual Constructors) – Factories – Clone – Exemplars Multi methods 30 –How can factories be implemented in C++ –How can we efficiently initialize a library/etc. in C++? © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Numbers: Factories (Virtual Constructor) Why? – Want to create object from data describing the object – Type of object depends on the data itself Example – Parse in different number types – Number *n = new Number("(1:2)"); 31 Number inherits Complex Integer Fraction (re,im) i (c:d) © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Virtual Constructors: Factory (Usage) main.cc #include "NumberFactory.h" #include "fraction.h" 32 int main(int argc, char *argv[]) { Number n1=NumberFactory::make("(1:2)"); Number n2=NumberFactory::make("(1,2)"); cout << n1+n2 << endl; } © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Virtual Constructors: Factory typedef number* make_t(const char *); NumberFactory.h class NumberFactory { static list<make_t*> cl; public: static void add(make_t *m) { cl.push_back(m); } static Number make(const char *nbr) { Number *r=NULL; list<make_t*>::const_iterator b=cl.begin(), e=cl.end(); while(b!=e && (r=(*b)(nbr))==NULL) ++b; return Number(r); } }; template<class T> class NumberFH { public: NumberFH(make_t *m=(make_t*)&T::make) { NumberFactory::add(m); } }; 33 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies number.h class fraction : public number { public: fraction.cc class number { public: virtual ~number() {} }; fraction.h Virtual Constructors: Factory (cont‘d) 34 // make can also be protected => factory must be friend // of this class. static fraction *make(const char *nbr); }; #include "NumberFactory.h" #include "number.h" static NumberFH<fraction> register_fraction; © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Virtual Constructors: Factory (cont‘d) number.o cl number inherits 35 complex integer fraction complex ::make integer ::make fraction ::make complex.o integer.o fraction.o © 2013 IBM Corporation Virtual Constructors: Exemplars class Exemplar {}; class Number { static list<Number *>cl; public: Number.h Not for the exam – Virtual Constructors as introduced by Coplien IBM Research – Zurich Business Integration Technologies 36 Number(Exemplar) { cl.push_back(this); } virtual ~Number() {} virtual Number *make(const char *nbr)=0; static Number *make_nbr(const char *nbr) { Number *r=NULL; list<Number*>::const_iterator b=cl.begin(), e=cl.end(); while(b!=e && (r=b->make(nbr))==NULL) ++b; return r; } }; © 2013 IBM Corporation Fraction.h Virtual Constructors: Exemplars (cont‘d) class Fraction: public Number { public: Fraction(Exemplar dummy) : Number(Exemplar()) {} Fraction *make(const char *nbr); }; #include "Fraction.h" Fraction.cc Not for the exam – Virtual Constructors as introduced by Coplien IBM Research – Zurich Business Integration Technologies 37 static Fraction *exemplar=new Fraction(Exemplar()); Fraction *Fraction::make(const char *nbr) { // If nbr points to a valid fraction, create // a Fraction object and return it. // If not, return NULL. } // … © 2013 IBM Corporation Not for the exam – Virtual Constructors as introduced by Coplien IBM Research – Zurich Business Integration Technologies Virtual Constructors: Exemplars (cont‘d) number.o cl Number inherits Complex 38 Integer Fraction Complex *exempla r Integer *exempla r Fraction *exempla r complex.o integer.o fraction.o © 2013 IBM Corporation Virtual Constructors: Exemplars (Usage) #include "Number.h" #include “fraction.h" main.cc Not for the exam – Virtual Constructors as introduced by Coplien IBM Research – Zurich Business Integration Technologies 39 int main(int argc, char *argv[]) { Number *n1=Number::make_nbr("(1:2)"); Number *n2=Number::make_nbr("(1,2)"); cout << (*n1)+(*n2) << endl; } © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Factories: Summary Table that contains a list of (member) functions that creates other objects Table is created through “special” constructors => No initialization necessary in the main function !Attention! Construction of table depends on the link order of the object files. The type of object created should not depend on this. Write clean code. 40 © 2013 IBM Corporation Not for the exam – clone sometimes considered as Virtual Constructors IBM Research – Zurich Business Integration Technologies Virtual Constructor (clone) Constructors cannot be virtual themselves Only methods and destructors may be virtual Solution: Implement a virtual method that invokes the constructor class Number { public: virtual Number *clone()=0; }; class Integer: public Number { public: virtual Integer *clone() { return new Integer(*this); } }; 41 © 2013 IBM Corporation Not for the exam – clone sometimes considered as Virtual Constructors IBM Research – Zurich Business Integration Technologies Virtual Constructors: new Overload the new Operator Problems – Object to be created is not known a-priori – Required memory space is not known either Solution – New memory allocator that does not change the address of the object during a realloc Details? – See Jim Coplien. Advanced C++ Programming Styles and Idioms. Addison-Wesley. 42 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Literature Jim Coplien. Advanced C++ Programming Styles and Idioms. Addison-Wesley. 43 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Agenda C++11 Factories (Virtual Constructors) – Factories – Clone – Exemplars Multi methods –C++ can be used in many different ways –Very static with templates –Very dynamic as this discussion has shown –Makes C++ suitable to integrate programming languages that follow different paradigms 44 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Multi Methods (Sometimes referred to as Multi Dispatch) Operator depends on multiple arguments (not only the this “argument”) It should be possible to add functions posteriori Solution – Use a function table – Invoke the function based on the types of arguments 45 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Multi Methods, finding the method class Number { protected: static map<opdesc,op_t*> ops; public: // … Describes name of function and argument types // function types // methods to register new functions // Number operator+(const Number &b) const { op_t *op=ops[opdesc('+',typeid(*n),typeid(*(b.n)))]; if(op==NULL) throw runtime_error(); return Number(op(n,b.n)); } // ... }; 46 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Multi Methods: Definition of Function Table class Number { protected: struct opdesc { char op; const type_info *a1, *a2; opdesc(char o, const type_info &arg1, const type_info &arg2) : op(o), a1(&arg1), a2(&arg2) {} bool operator==(const opdesc &o) const { return op==o.op && (*a1)==*(o.a1) && (*a2)==*(o.a2); } bool operator<(const opdesc &o) const { // define a < relation between opdescs } }; static map<opdesc,op_t*> ops; // … 47 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Multi Methods: Registration of new functions class Number { public: // … typedef number *(op_t)(const number*,const number*); static void register_op(char opname, const type_info &arg1, const type_info &arg2, op_t *op) { ops[opdesc(opname,arg1,arg2)]=op; } Number operator+(const Number &b) const { op_t *op=ops[opdesc('+',typeid(*n),typeid(*(b.n)))]; if(op==NULL) throw runtime_error(); return Number(op(n,b.n)); } // … }; 48 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Multi Methods: Summary 1. Definition of a “two-dimensional” function table 2. Wrapper for +/-/*/… operators that identifies actual function based on function table 3. Registration of the functions like it is done for the factory New data type? – Create a new subclass of number – Register its methods in the function table (ops) New operator? – Just register the new methods in the function table (ops) 49 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Summary C++11 Factories (Virtual Constructors) – Factories – Clone – Exemplars Multi methods 50 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Exercise 1 Improve the computer player of your “Connect 4” implementation and make sure you can accommodate computer players from your colleagues. Get at least 3 computer players from other colleagues and see who is better. In order to be able to do this you need to do the following: – You need to implement a class religiously implementing the playfield interface that gives the computer player access to the playfield through the stoneat(x,y) function. – Your computer player needs to implement the player interface that provides theplay(playfield) method. In case the functionality provided by the playfield interface is not sufficient, your player needs to convert the playfield into your own representation. – Make sure that your program works in such a way that two computer players can play against each other. – Do not change the playfield or player interfaces or otherwise your implementation will not work with your colleagues’ implementations. 51 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Exercise 2 Compute gcf as Template-Meta-Program and with constexpr Implement the greatest common factor (use Euclid’s algorithm) as a template meta program and using constexpr functions (as we did in the last lecture for primes). 52 © 2013 IBM Corporation IBM Research – Zurich Business Integration Technologies Questions? No more questions, then see you next week 53 © 2013 IBM Corporation