1 parametric polymorphism polymorphism techniques c++ templates

Post on 21-Dec-2015

239 Views

Category:

Documents

4 Downloads

Preview:

Click to see full reader

TRANSCRIPT

1

Parametric PolymorphismParametric Polymorphism

Polymorphism Techniques C++ Templates

2

Polymorphism: OverloadingPolymorphism: Overloading

void f(int n) { cout << n << endl; }

struct Num { Num(int v = 0) : value(v) { } int value; };

void f(Num n) { cout << n.value << endl; }

int main(int, char**) { Num n(5); f(5); f(n); return 0;}

3

void f(int n) { cout << n << endl; }

struct Num { Num(int v = 0) : value(v) { } int value; operator int() { return value; } };

int main(int, char**) { Num n(5); f(5); f(n); return 0;}

Polymorhism: CoercionPolymorhism: Coercion

4

struct Num { Num(int v = 0) : value(v) { } int value;};

struct Num2 : Num { Num2(int v = 0) : Num(v) { } void add(int a) { value += a; }};

void f(Num& n) { cout << n.value << endl; }

int main(int, char**) { Num x(5); Num2 y(5); f(x); f(y); return 0;}

Polymorhism: Inclusion (Subtyping)Polymorhism: Inclusion (Subtyping)

5

struct Num { Num(int v = 0) : value(v) { } int value;};

struct Person { Person(const char* name) : value(name) { } const char* value;};

template<typename T>void f(T& t) { cout << t.value << endl; }

int main(int, char**) { Num x(5); Person y("Christopher Nolan"); f(x); f(y); return 0;}

Polymorphism: ParametricPolymorphism: Parametric

6

Taxonomy of PolymorphismTaxonomy of Polymorphism Ad-hoc

Overloading Coercion

Universal Inclusion (subtyping) Parametric

7

Parametric PolymorphismParametric Polymorphism

Allows definitions to be parametrized at compile-time Such a definition is actually a “function” that returns a

new program element(method, class, etc.)

Template parameters = The arguments of this “function” AKA: type parameters

Instantiation of a template == evaluating the “function”

8

Template Instantiation in C++Template Instantiation in C++

The compiler recompiles the template definition Substitutes the formal template parameters with the

actual parameters Generates new object code

Almost no compilation when the definition is read Most compilation work is at each instantiation point

The template definition must be part of the source code That is: #included from an .h file Supporting separate compilation of templates is too

difficult in practice

9

C++ Function TemplatesC++ Function Templates A function can have type parameters

template<typename T> T* create() { return new T(); } void f() { string* sp = create<string>(); }

we use create() thru explicit instantiation We specify the actual types inside a pair of < >

In some cases the compiler can deduce the template parameterstemplate<typename T> void print(T& t) { cout << t << endl; }void g() { print(5); }

We use print() thru implicit instantiation Possible if a type parameter is also a value parameter Make overload resolution more difficult Introduced later

10

A Template Taking a ConstantA Template Taking a Constanttemplate<int N>struct FixedArray { double values[N]; int size() { return N; } // Could be static};

int main(int, char**) { FixedArray<10> arr; arr.values[0] = 3.14; cout << "first element=" << arr.values[0] << endl; cout << "size=" << arr.size() << endl; return 0;}

11

A Template Taking a Function Pointer A Template Taking a Function Pointer template<void (*F)()>struct Caller { static void run() { F(); }};

void hi() { cout << "hi" << endl;}void bye() { cout << "bye" << endl; } int main(int, char**) { Caller<hi>::run(); Caller<bye>::run(); return 0;}

12

A Template Taking a TypeA Template Taking a Typetemplate<typename T>struct Pair { void set(const T& x, const T& y) { a = x; b = y; } void print() { cout << a << "," << b << endl; } private: T a, b; };

typedef Pair<char*> StrPair; // Instantiate Pair, // pass char* as an argument

typedef Pair<int> IntPair; int main(int, char**) { StrPair sp; IntPair ip; sp.set("ab", "cd"); sp.print(); ip.set(10, 20); ip.print(); return 0;}

13

SpecializationSpecializationtemplate<typename T>struct Pair { void set(const T& x, const T& y) { a = x; b = y; } void print() { cout << a << "," << b << endl; } private: T a, b; };

template<>struct Pair<bool> { void set(bool x, bool y) { v = (x?1:0) + (y?2:0); } void print() { cout << (v&1) << "," << (v&2) << endl; } private: int v;}; int main(int, char**) { Pair<bool> pb; pb.set(true, false); pb.print(); Pair<char> pc; pc.set('x', 'y'); pc.print(); return 0;}

14

A Non Conforming Specialization is LegalA Non Conforming Specialization is Legaltemplate<typename T>struct Pair { void set(const T& x, const T& y) { a = x; b = y; } void print() { cout << a << "," << b << endl; } private: T a, b; };

template<>struct Pair<bool> { void set(bool x, bool y) { v = (x?1:0) + (y?2:0); } public: int v;}; void doSomething() { Pair<bool> pb; pb.set(true, false); cout << pb.v << endl; Pair<char> pc; pc.set('x', 'y'); pc.print(); pb.print(); // Error. Pair<bool>::print() is undefined}

15

C++ Templates: Interim SummaryC++ Templates: Interim Summary A template is a “function”

Arguments: types, constants Return value : A new class or a new function

Recognized by the compiler

The template is recompiled with each Instantiation

Specialization == “if” Different result based on the actual arguments

16

C++ Templates as a C++ Templates as a Programming LanguagesProgramming Languages

Values?

Fundamental computational step?

What is the run-time phase?

What is the compile-time phase?

Type system: static or dynamic?

How powerful is it? What programs can we write in this language?

We saw that we can do “if” decisions

What sorts of bugs does it have? Can we run into an infinite loop?

17

A Non-Terminating CompilationA Non-Terminating Compilationtemplate<typename T>struct Loop{ typedef typename Loop<Loop<T> >::Temp Temp;};

int main(int, char**) { Loop<int> n; return 0;}

18

ConceptsConcepts Concept = A set of requirements that a type (or a set of

types) should uphold Requirements == constraints == expectations A generalization of structural conformance

A C++ template defines a concept Expects its type parameter to support certain operations

But the concept is implict The requirements are not clearly defined They are hidden inside the code

19

Modelling the “From” ConceptModelling the “From” Concept

template<typename From, typename To>void copy(From& f, To& t) { for(int x = f.get(); !f.eof(); x = f.get()) t.put(x); }

struct Range { char begin, end; Range(char b, char e) : begin(b), end(e) { } int get() { return eof() ? -1 : begin++; } bool eof() { return begin > end; }};

int main(int, char**) { Range r('a', 'z'); copy(r, cout); return 0;}

20

Same Program w/ InheritanceSame Program w/ Inheritancevoid copy(istream& f, ostream& t) { for(int x = f.get(); !f.eof(); x = f.get()) t.put(x);}

struct Range : istream { // Does istream have virtual methods?

// If it does, which one of these should be overridden

// int istream::peek() ?

// int istream::get()

// istream& istream::ignore(streamsize num, int delim) ?

// istream& istream::get(char& c)

// istream& istream::putback(char c)

// istream& istream::unget(char c)

// istream& istream::get(char* buf)

// istream& istream::getline(char* buf, streamsize num)

// istream& istream::read(char* buf, streamsize num)

// streamsize istream::gcount()

// pos_type istream::tellg()

// ... }

21

Cryptic Error Messages Cryptic Error Messages struct Num { // The problem: Copy c'tor is not public public: Num() { } private: Num(const Num& other) { } };

template<typename T> void f() { vector<vector<T> > matrix; matrix.push_back(vector<T>()); }

template<typename V,typename T>void g() { (new V())->push_back(T()); }

template<typename V,typename T>void hh() { g<V,T>(); f<T>(); }

template<typename T>void h() { hh<vector<Num>,T>(); }

int main(int, char**) { h<Num>(); return 0; }

22

Cryptic Error Messages (Cont.)Cryptic Error Messages (Cont.)x.cpp: In function ‘void std::_Construct(_T1*, const _T2&) [with _T1 = Num, _T2 = Num]’:

/usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_uninitialized.h:86: instantiated from ‘_ForwardIterator std::__uninitialized_copy_aux(_InputIterator, _InputIterator, _ForwardIterator, __false_type) [with _InputIterator = __gnu_cxx::__normal_iterator<const Num*, std::vector<Num, std::allocator<Num> > >, _ForwardIterator = Num*]’

/usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_uninitialized.h:113: instantiated from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const Num*, std::vector<Num, std::allocator<Num> > >, _ForwardIterator = Num*]’

/usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_uninitialized.h:254: instantiated from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>) [with _InputIterator = __gnu_cxx::__normal_iterator<const Num*, std::vector<Num, std::allocator<Num> > >, _ForwardIterator = Num*, _Tp = Num]’

/usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_vector.h:234: instantiated from ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = Num, _Alloc = std::allocator<Num>]’

x.cpp:15: instantiated from ‘void f() [with T = Num]’

x.cpp:22: instantiated from ‘void hh() [with V = std::vector<Num, std::allocator<Num> >, T = Num]’

x.cpp:25: instantiated from ‘void h() [with T = Num]’

x.cpp:27: instantiated from here

x.cpp:9: error: ‘Num::Num(const Num&)’ is private

/usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_construct.h:81: error: within this context

x.cpp: In function ‘void std::_Construct(_T1*, const _T2&) [with _T1 = Num, _T2 = Num]’:

/usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_uninitialized.h:86: instantiated from ‘_ForwardIterator std::__uninitialized_copy_aux(_InputIterator, _InputIterator, _ForwardIterator, __false_type) [with _InputIterator = __gnu_cxx::__normal_iterator<const Num*, std::vector<Num, std::allocator<Num> > >, _ForwardIterator = Num*]’

...

23

Documenting ConceptsDocumenting Concepts Understanding the type requirements from error messages

is very difficult

The solution: documentation

Here are two examples from SGI's STL web-site http://www.sgi.com/tech/stl/Assignable.html http://www.sgi.com/tech/stl/UnaryFunction.html

24

Concept: AssignableConcept: Assignable

Refinement of-

Modelsint

NotationX A type that is a model of Assignablex,y Object of type X

Valid expressionsName Syntax Return-type Post-condition=======================================================Copy-ctor X(x) X X(x) is a copy of xAssignment x = y X& x is a copy ySwap swap(x,y) void Same as: X t = x; x = y; y = t

25

Concept: Unary FunctionConcept: Unary FunctionRefinement of

Assignable

Modelschar (*)(int)

NotationF A type that is a model of Unary FunctionX The argument type of FResult The result type of Ff Object of type Fx Object of type X

Valid expressionsName Syntax Return-type Post-condition=======================================================call f(x) Result Return value is in f's range

26

Concept: PrintableConcept: PrintableRefinement of

Assignable

Models???

NotationP,Q A type that is a model of Printablep,q Object of type P, Q (respectively)

Valid expressionsName Syntax Return-type Post-condition=======================================================ctor P p(q) P p has a copy of qprint p.print() void print() sent to p's copy of q

27

Can we Model the Printable Concept?Can we Model the Printable Concept?

class Printable { ??? };

struct X { print() { ... } };struct Y { print() { ... } };struct Z { };

vector<Printable> vec;vec.push_back(X()); // OK -- X defines print() vec.push_back(Y()); // OK -- Y defines print() Printable& p = vec[0]; // p references the copy of x inside the vectorp.print(); // invoke print() on that copy of xp = vec[1]; // p references the copy of y inside the vector

vec.push_back(Z()); // Error -- Z does not defines print() Printable q = Z(); // Error -- Z does not defines print()

28

Printable: the void* ApproachPrintable: the void* Approachstruct Printable void* data;

template<typename T> Printable(const T& t) { t.print(); // Ugly data = new T(t); }

virtual ~Printable() { delete data; } // Cannot delete a void pointer

Printable(const Printable& p) { data = new ??? p.data; } // Which type to create?

void print() { data -> ??? } // Cannot send print() to void* // Cannot downcast - there's no common superclass};

29Printable: the Right WayPrintable: the Right Waystruct Base { virtual ~Base() { } virtual Base* clone() const = 0; virtual void print() = 0;};

template<typename U>struct Holder : Base { U* u; Holder(const U& arg) : u(new U(arg)) { } ~Holder() { delete u; } Base* clone() const { return new Holder(*u); } void print() { u->print(); }};

struct Printable { Base* holder;

template<typename T> Printable(const T& t) : holder(new Holder<T>(t)) { }

Printable(const Printable& p) : holder(p.holder->clone()) { }

virtual ~Printable() { delete holder; } void print() { holder->print(); }};

30

Printable: The Assignment OperatorPrintable: The Assignment Operator

struct Printable {

Base* holder; ...

void swap(Printable& p) { Base* temp = holder; holder = p.holder; p.holder = temp; }

Printable& operator=(const Printable& p) { Printable temp(p); swap(temp); return *this; }};

31Is This a Good Alternative? No!Is This a Good Alternative? No!

struct Printable {

Base* holder; ...

Printable& operator=(const Printable& p) { if(this == &p) return *this; delete holder; holder = p.holder.clone(); return *this; }};

32

C++ Templates: Main PointsC++ Templates: Main Points

A functional programming language

Turing complete Has the same power as any other programming

language

Runs while the compiler compiles We build a tree (of types) at compile-time When an error occurs the compiler prints the path from

the root of this tree

33

C++ Polymorphism: C++ Polymorphism: Templates vs. InheritanceTemplates vs. Inheritance

Templates - pros: Type safety - as in collections

Parametrize the protocol of a class Easier to define a compatible type

Require only structural conformance A type parameter can serve as a super class

Mixin Faster

Inheritance – pros: Polymorphism happens at run-time

E.g.: type of object is determined by an input value Readable error messages No executable blow up Separate compilation (templates must be #included)

top related