Generic Programming in C Giuseppe Attardi Universit di

  • Slides: 29
Download presentation
Generic Programming in C++ Giuseppe Attardi Università di Pisa

Generic Programming in C++ Giuseppe Attardi Università di Pisa

Generic Parameters l Function for squaring a number: sqr(x) { return x * x;

Generic Parameters l Function for squaring a number: sqr(x) { return x * x; } l C version: int sqr(int x) { return x * x; } l Multiple versions: int sqr. Int(int x) { return x * x; } l C++ overloading: int sqr(int x) { return x * x; } double sqr(double x) { return x * x; }

C++ Templates template<class T> T sqr(T x) { return x * x; } l

C++ Templates template<class T> T sqr(T x) { return x * x; } l Compiler automatically generates a version for each parameter type used by a program: int a = 3; double b = 3. 14; int aa = sqr(a); double bb = sqr(b);

Notes: macros’ limits #define sqr(x) ((x) * (x)) int aa = sqr(a++); int aa

Notes: macros’ limits #define sqr(x) ((x) * (x)) int aa = sqr(a++); int aa = ((a++) * (a++)); int aaa = sqr(aa); #define fact(n) (n == 0) ? 1 : fact(n-1) * n #define SQR(T) T sqr(T x) {return x * x; } SQR(int); SQR(double); sqr(a); sqr(b);

Other types as well? class Complex { double real, imag; Complex operator *(Complex y)

Other types as well? class Complex { double real, imag; Complex operator *(Complex y) { return Complex( real * y. real – imag * y. imag, real * y. imag + imag * y. real); } } Complex c(1, 2); Complex cc = sqr(c);

Compile time checking template<class T> void swap(T& a, T& b) { const T temp

Compile time checking template<class T> void swap(T& a, T& b) { const T temp = a; a = b; b = temp; } int a = 5, b = 9; int& ar = a; int* ap = *a; ar++; *ap = (*ap)+1 swap(a, b); // OK double c = 9. 0; swap(a, c); // error

C++ Standard Template Library l l l Goal: represent algorithms in as general form

C++ Standard Template Library l l l Goal: represent algorithms in as general form as possible without compromising efficiency Extensive use of templates Only uses static binding (an inlining) Use of iterators for decoupling algorithms from containers Iterator: abstraction of pointers

STL Organization l Containers § vector, deque, list, set, map, … l Algorithms §

STL Organization l Containers § vector, deque, list, set, map, … l Algorithms § for_each, find, transform, sort l Iterators § forward_iterator, reverse_iterator, istream_iterator, … l Function Objects § plus, equal, logical_and, project 1 l Allocators

Examples from C++ Annotations vector l inner_product l sort l

Examples from C++ Annotations vector l inner_product l sort l

Forward Iterator int main(int argc, char **argv) { vector<string> args(argv, argv + argc); vector<string>:

Forward Iterator int main(int argc, char **argv) { vector<string> args(argv, argv + argc); vector<string>: : iterator iter = args. begin(); for ( ; iter != args. end(); ++iter ) cout << *iter << " "; cout << endl;

Reverse Iterator vector<string>: : reverse_iterator iter = args. rbegin(); for (; iter != args.

Reverse Iterator vector<string>: : reverse_iterator iter = args. rbegin(); for (; iter != args. rend(); ++iter ) cout << *iter << " "; cout << endl;

Inner Product vector<unsigned> ia 1 = {1, 2, 3, 4, 5, 6, 7}; vector<unsigned>

Inner Product vector<unsigned> ia 1 = {1, 2, 3, 4, 5, 6, 7}; vector<unsigned> ia 2 = {7, 6, 5, 4, 3, 2, 1}; // dot product inner_product(ia 1. begin(), ia 1. end(), ia 2. begin(), 0);

inner_product: definition T inner_product(Input. Iterator first 1, Input. Iterator last 1, Input. Iterator first

inner_product: definition T inner_product(Input. Iterator first 1, Input. Iterator last 1, Input. Iterator first 2, T init, Binary. Function op 1, Binary. Function op 2); l Initializes result to init l For each iterator i 1 in [first 1, last 1), and i 2 = first 2 + (i 1 - first 1)) updates result as follows: result = op 1(result, op 2(*i 1, *i 2))

Inner Product (string concat) class Join { public: Join(string const &sep): sep(sep) {} string

Inner Product (string concat) class Join { public: Join(string const &sep): sep(sep) {} string operator()(string const& s 1, string const& s 2) { return s 1 + sep + s 2; } private: string sep; }; vector<string> names 1= {"Frank", "Karel", "Piet"}; vector<string> names 2 = {"Brokken", "Kubat", "Plomp"}; inner_product(names. begin(), names 1. end(), names 2. begin(), "t“, Join("n"), Join(" ")) ;

Parametrized Bubblesort template<class T> struct greater { bool operator()(const T& a, const T& b)

Parametrized Bubblesort template<class T> struct greater { bool operator()(const T& a, const T& b) const { return a > b; }; template<class T> struct less { bool operator()(const T& a, const T& b) const { return a < b; };

Note int x = 3, y = 5; bool b = greater<int>()(x, y);

Note int x = 3, y = 5; bool b = greater<int>()(x, y);

Function object version template<class T, class C=less<T> > void bubblesort(vector<T> a, const C& comp)

Function object version template<class T, class C=less<T> > void bubblesort(vector<T> a, const C& comp) { for (unsigned i = 0; i < a. size(); ++i) for (unsigned j = i+1; j < a. size(); ++j) if (comp(a[i], a[j])) swap(a[i], a[j]); };

Traditional Functional version template<class T> void bubblesort(vector<T> a, bool (*comp)(T&, T&)) { for (unsigned

Traditional Functional version template<class T> void bubblesort(vector<T> a, bool (*comp)(T&, T&)) { for (unsigned i = 0; i < size; ++i) for (unsigned j = i+1; j < size; ++j) if (comp(a[i], a[j])) swap(a[i], a[j]); };

Bubblesort usage vector<int> x = {1, 5, 3, 4, 2}; bubblesort(x, greater<int>()); bubblesort(x, less<int>());

Bubblesort usage vector<int> x = {1, 5, 3, 4, 2}; bubblesort(x, greater<int>()); bubblesort(x, less<int>()); // implicit type inference vector<float> f; bubblesort(f); // uses less<float>

(Partial) Template Specialization an alternate version of the class template code can be provided

(Partial) Template Specialization an alternate version of the class template code can be provided for certain template parameters template<> void bubblesort<vector<string&> >(…) { } l l partial specialization: when only some of the template parameters are supplied

Template Enumeration template<class T> class Vector : public vector<T> { public: Enumeration get. Enumeration()

Template Enumeration template<class T> class Vector : public vector<T> { public: Enumeration get. Enumeration() { return (Enumeration(*this)); } class Enumeration { … } };

Enumeration (2) class Enumeration { private: vector<T> const& v; unsigned idx; public: Enumeration(vector<T> const&

Enumeration (2) class Enumeration { private: vector<T> const& v; unsigned idx; public: Enumeration(vector<T> const& vector) : v(vector), idx(0) { } T const& next() { // uses T if (idx == vp. size()) throw No. Such. Element. Exception(index); return vp[idx++]; } bool has. Next() { return idx < vp. size(); } };

Enumeration (3) Vector<int> vector; … Vector<int>: : Enumeration en = vector. get. Enumeration(); while

Enumeration (3) Vector<int> vector; … Vector<int>: : Enumeration en = vector. get. Enumeration(); while (en. has. Next()) cout << en. next() << endl;

Issue: template instantiation // file sum. Vector. h template <class T> T sum. Vector(T*

Issue: template instantiation // file sum. Vector. h template <class T> T sum. Vector(T* array, unsigned n) { T sum(0); for (int i = 0; i < n; ++i) sum += array[i]; return sum; }

Instantiation (1) #include "sum. Vector. h" int x[] = {1, 2}; double y[] =

Instantiation (1) #include "sum. Vector. h" int x[] = {1, 2}; double y[] = {1. 1, 2. 2}; cout << sum. Vector(x, 2) << endl << sum. Vector(y, 2) << endl; l If the same template function definition is included in different source files, separately compiled and linked, there will be only one instantiation, per type of template function

Instantiation (2) l declare a template function, if it is known that the required

Instantiation (2) l declare a template function, if it is known that the required instantiation is available in another source file: template<class T> T sum. Vector(T* tp, unsigned n); int v[] = {1, 2}; sum. Vector(v, 2);

Instantiation (3) l declare template functions in header files, keep the definition in a

Instantiation (3) l declare template functions in header files, keep the definition in a template source file: template<class T> T sum. Vector(T* tp, unsigned n) { return *tp; } static void* p 1 = static_cast<int (*)(int*, unsigned)>(sum. Vector);

Forcing Instantiations static void* p[] = { static_cast<int (*)(int*, unsigned)>(sum. Vector), static_cast<double (*)(double*, unsigned)>(sum.

Forcing Instantiations static void* p[] = { static_cast<int (*)(int*, unsigned)>(sum. Vector), static_cast<double (*)(double*, unsigned)>(sum. Vector), static_cast<unsigned (*)(unsigned *, unsigned)>(sum. Vector) };

Explicit Instantiations template<class T> T sum. Vector(T *tp, unsigned n) { return (*tp); }

Explicit Instantiations template<class T> T sum. Vector(T *tp, unsigned n) { return (*tp); } template int sum. Vector<int>(int *, unsigned); template double sum. Vector<double>(double *, unsigned); template unsigned sum. Vector<unsigned>(unsigned *, unsigned);