Generic Programming in C Giuseppe Attardi Universit di
























![Instantiation (1) #include "sum. Vector. h" int x[] = {1, 2}; double y[] = Instantiation (1) #include "sum. Vector. h" int x[] = {1, 2}; double y[] =](https://slidetodoc.com/presentation_image_h2/e9967877116e8eefda0963e6b33b9d66/image-25.jpg)


![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.](https://slidetodoc.com/presentation_image_h2/e9967877116e8eefda0963e6b33b9d66/image-28.jpg)

- Slides: 29

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

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 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 = ((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) { 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 = 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 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 § 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

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. rend(); ++iter ) cout << *iter << " "; cout << endl;

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 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 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) 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);

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 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>()); // 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 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() { return (Enumeration(*this)); } class Enumeration { … } };

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 (en. has. Next()) cout << en. next() << endl;

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[] =](https://slidetodoc.com/presentation_image_h2/e9967877116e8eefda0963e6b33b9d66/image-25.jpg)
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 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 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 staticcastint int unsignedsum Vector staticcastdouble double unsignedsum Forcing Instantiations static void* p[] = { static_cast<int (*)(int*, unsigned)>(sum. Vector), static_cast<double (*)(double*, unsigned)>(sum.](https://slidetodoc.com/presentation_image_h2/e9967877116e8eefda0963e6b33b9d66/image-28.jpg)
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); } template int sum. Vector<int>(int *, unsigned); template double sum. Vector<double>(double *, unsigned); template unsigned sum. Vector<unsigned>(unsigned *, unsigned);