Templates and Generic Programming - PowerPoint PPT Presentation

1 / 55
About This Presentation
Title:

Templates and Generic Programming

Description:

The code that implements a linked list is independent of ... A parameterized type is a user-defined type who's properties are based in ... even (mistakenly? ... – PowerPoint PPT presentation

Number of Views:71
Avg rating:3.0/5.0
Slides: 56
Provided by: usersEc3
Category:

less

Transcript and Presenter's Notes

Title: Templates and Generic Programming


1
Templates and Generic Programming
2
Motivating Problem
  • Containers or data structures have behavior
    that is independent of the type of object
    contained within them.
  • The code that implements a linked list is
    independent of whether integers or strings are
    stored in the list.
  • Wed like to be able produce just one
    implementation of a linked list and re-use that
    implementation.

3
Parameterized Types(template classes)
  • A parameterized type is a user-defined type whos
    properties are based in part upon parameters
  • For a container, the parameter is the type of
    object to be stored inside the container.
  • ADA and C are two examples of languages which
    provide support for parameterized types (Java
    does not have this feature).

4
Example of C Template Class
  • template lttypename ElementTypegt
  • class Vector
  • ElementType array
  • public
  • explicit Vector(int size)
  • ElementType operator(unsigned)
  • const ElementType operator(unsigned) const
  • The formal parameter ElementType can be used
    anywhere in the definition of the class.
  • When the class is instantiated, an actual type
    (the argument), for example int or double will be
    bound to the formal parameter.

5
An Alternative
  • Use a generic object type (e.g., void from C,
    or Object from Java).
  • class List
  • void value
  • List next
  • main()
  • List myList
  • myList.insert((int) 42)
  • x (int) myList.remove()

6
Evaluation of Generic Object Containers
  • Advantages
  • Exactly one copy of source code and one copy of
    object code no matter how many times the
    container is re-used.
  • Can be polymorphic (different types stored
    inside same container).
  • Disadvantage
  • No static type checking when objects are inserted
    or removed to/from the container

7
Macros, the poor mans Parameterized Type
  • A second alternative involves defining a macro.
  • An expansion of the macro produces the desired
    code for the container
  • define List(X) \
  • class ListX \
  • X value \
  • ListX next \
  • List(int) // expand the macro for X int
  • main()
  • Listint myList // notice how name is formed
  • Listint.insert(42)

8
Evaluation of Macros
  • Advantage
  • Requires no language support, can be used in any
    language
  • Disadvantages
  • Face it, this is a kludge, the syntax and use is
    ugly and error prone.
  • No syntax checking of macros (until theyre
    expanded).
  • Programmer must be careful to avoid expanding the
    macro the same way twice (and getting
    multiply-defined symbols).

9
Template Terminology
  • Template Definition
  • Specifying the template, including its parameter
    list and the definition of the class.
  • Template Instantiation
  • as a noun, this term refers to the resulting type
    (class) that is created by binding arguments to
    all of the type parameters for the template
  • as a verb, this term refers to the process of
    creating the instantiated template.
  • Template declaration
  • rarely used. declares the name of the template
    and the parameter list but does not specify the
    class.

10
The Template Instantiation Process
  • Template instantiation is performed statically
    (i.e., at compile time).
  • Every distinct set of arguments results in a
    completely distinct instantiated class
  • Each member function is recreated and compiled
    for each new type.
  • Some compilers may even (mistakenly?)
    recreate/recompile member functions each time the
    template is instantiated (even if the arguments
    are the same!)

11
Textual Substitution
  • It is usually adequate to visualize template
    instantiation as a textual expansion of the
    template with parameters replaced by arguments
    (like a macro).
  • A good compiler will minimize the redundant code
    produced.
  • Theres more error checking with templates than
    you could ever get with macros, however.

12
The .h file gotcha
  • Template definitions need to go inside .h files,
    including definitions of all member functions.
  • a .cc file consisting of function member function
    definitions for a template class will produce an
    empty .o file!
  • the compiler does not use the template definition
    until a type is actually instantiated.
  • your Vector.cc file cannot possibly instantiate
    all the types of Vectors I might ultimately
    decide to use in my application.

13
Example Using Templates
  • template lttypename Fst, typename Sndgt
  • class Pair
  • public
  • Fst first
  • Snd second
  • main(void)
  • Pairltint, chargt aPair // binds Fst to int,
    Snd to char
  • aPair.first 42
  • aPair.second hello world
  • Pairltdouble, chargt anotherPair // a different
    type!
  • anotherPair aPair // error! different types!

14
Template Functions
  • In addition to the parameterized-type feature,
    C also supports functions with type parameters
    (template functions).
  • Note technically, the member functions in a
    template class are actually template functions.
  • template lttypename ElementTypegt
  • VectorltElementTypegtVector(int sz) size(sz)
  • array new ElementTypesize

15
Useful Template functions
  • template lttypename Tgt
  • T min(const T x, const T y)
  • if (y lt x) return y
  • else return x
  • template lttypename Tgt
  • void swap(T x, T y)
  • T t x
  • x y
  • y t
  • These functions are in ltalgorithmgt
  • The functions can be instantiated implicitly
    (more on this later) or explicitly swapltintgt(a,
    b)

16
How to write operator
  • Now, we can use template functions to simplify
    the work of operator overloading.
  • never (almost never) write an operator member
    function for your class.
  • instead always (almost always) write an
    operator function.
  • template lttypename Tgt
  • T operator(const T x, const T y)
  • T t(x)
  • t y
  • return t

17
Relationals
  • Similarly, theres no reason to every write a !
    member function, or a gt member function.
  • The standard include file ltfunctionalgt and
    ltalgorithmgt defines
  • template lttypename Tgt
  • bool operator!(const T x, const T y)
  • return ! (x y)
  • Greater than is defined using less than.
  • You only need to write operatorlt and operator
    for your classes.

18
Templates in Templates
  • The C standard allows a template class to have
    template functions inside it. This feature is
    very useful for type conversions
  • template lttypename ElementTypegt
  • class Vector
  • template lttypename OtherElementTypegt
  • Vector(const VectorltOtherElementTypegt v)
  • copy(v)

19
Syntax for Member Templates
  • template lttypename ElementTypegt
  • template lttypename OtherElementTypegt
  • void VectorltElementTypegtcopy(const
  • VectorltOtherElementTypegt other)
  • size other.getSize()
  • array new ElementTypesize
  • for (unsigned k 0 k lt size k 1)
  • arrayk // other.arrayk oops!
  • otherk
  • NOTE you cannot access the private members of
    the other type!

20
Template Functions and Implicit Instantiation
  • Recall that instantiation refers to the binding
    of type arguments to the template parameters and
    generating the actual C code.
  • With functions (only) in C the binding of
    arguments to parameters can be implicit.
  • The basic rule is that the compiler must be able
    to figure out what the arguments would be.
  • The technical rule is all implicitly determined
    template parameters must appear in the parameter
    list (function parameters) of the function.

21
Implicit Instantiation Examples
  • template ltclass Tgt
  • T safe_dereference(T ptr)
  • if (ptr 0) throw null_ptr_exception()
  • else return ptr
  • int p
  • safe_derefence(p) 42 // OK, T is bound to int
  • This template can be instantiated implicitly
    because the type param (T) can easily be
    determined from the function param (p). Since p
    is int, then T must be int

22
Implicit Instantiation Again
  • template lttypename Tgt
  • T nil(void)
  • return 0
  • int p nil() // sorry, no dice.
  • int q nilltintgt() // OK like this, tho.
  • By contrast, this re-implementation of the nil
    template (as a function, rather than as a class)
    cannot be instantiated implicitly. The return
    type is not sufficient for the compiler to guess
    T.
  • The reason for this rule is the same as the
    reasons behind the function overloading rule.
    Do you remember what that reason is?

23
Mixing Implicit and Explicit
  • It is possible to mix explicit and implicit
    instantiation with templates.
  • template lttypename NewType, typename CurrTypegt
  • NewType static_cast(CurrType p)
  • return (NewType) p
  • int p
  • char q static_castltchargt(p)
  • The implicit params must be at the end of the
    parameter list (CurrType in this example).
  • Normal rules apply for implicit params.

24
Non-Type Template Parameters
  • Template parameters can be objects as well as
    types.
  • The argument bound to these parameters must be a
    constant expression of the appropriate type.
  • Including pointers to functions (or even pointers
    to member functions) can be used.

25
Non-Type Example
  • template lttypename T, int sizegt
  • class FixedArray
  • T arraysize
  • public
  • T operator(unsigned k)
  • if (k lt size) return arrayk
  • else throw stdout_of_range()
  • FixedArrayltint, 100gt x

26
Default Arguments for Template Parameters
  • There is no need for default arguments for
    template functions because we have overloading
  • template lttypename X, typename Ygt
  • void is_same(const X x, const Y y)
  • cout ltlt x and y are different\n
  • template lttypename Tgt
  • void is_same(const T x, const T y)
  • cout ltlt x and y are the same\n

27
Defaults Arguments for Template Classes
  • Since there is no overloading of classes,
    template parameters can have default arguments in
    template classes.
  • template lttypename T, int size100gt
  • class FixedArray
  • T arraysize
  • public
  • T operator(unsigned k)
  • if (k lt size) return arrayk
  • else throw stdout_of_range()
  • FixedArrayltintgt x // 100 elements

28
Generic Programming
  • Objectives
  • Produce a library of algorithms and data
    structures.
  • Generic with respect to element type.
  • Generic w.r.t container type.
  • Predictable time complexity.
  • Competitive performance with customized code.

29
Representing a Data Structure
  • The C and the Java library illustrate the two
    leading ways to abstract a container.
  • Java uses the traditional abstract container
    approach
  • C uses sequences of cursor objects.
  • In C we use an auxiliary class object called an
    iterator. Each container type should have a
    companion iterator.

30
Iterators Are Like Pointers
  • Iterators are generalizations of pointers.
  • The iterator for an array container is actually a
    pointer (not a class).
  • The fundamental operations on a pointer are
  • dereference
  • increment
  • compare for equivalence
  • decrement
  • add an integer (increment multiple)
  • calculate difference of two iterators (subtract)

31
Forward, Bidirectional, and Random Access
Iterators
  • Not all data structures permit all pointer
    operations to be implemented efficiently.
  • Linear time to add multiple in linked list.
  • Linear time to decrement in singly-linked list.
  • Some algorithms (e.g., binary search) may require
    more capabilities than others (e.g., sort).
  • Binary search should be O(logN), but will only
    happen if all pointer ops are implemented with
    constant time.
  • When defining a data structure, you are
    responsible for identifying whether your iterator
    is forward, bidirectional or random access.

32
Naming Conventions for Iterators
  • To represent a container we use two iterators,
    one that points to the first element and one
    that points to the first position after the
    last element.
  • Every container (i.e., data structure) must
    define two functions one called begin() and one
    called end().
  • Ever container must define a nested type called
    iterator (note lower case) for the iterator.

33
A Simple Example
  • For the simplistic Vector example, all we need is
    to typedef ElementType to iterator.
  • typedef ElementType iterator
  • typedef const ElementType const_iterator
  • iterator begin(void) return array0
  • iterator end(void) return
  • arraynum_elements
  • const_iterator begin(void) const return
  • array0
  • const iterator end(void) const return
  • arraynum_elements

34
Generic Bubble Sort
  • template lttypename FIgt
  • void bubbleSort(FI begin, FI end)
  • if (begin end) return
  • for (FI i begin i ! end i)
  • FI j i
  • FI k j
  • k
  • while (k ! end)
  • if (j gt k) swap(j, k)
  • j
  • k

35
Success?
  • Note that the generic bubble sort can be invoked
    on VectorltTgt for any T.
  • Also on T for any T or LinkedListltTgt for any T!
  • Provided our optimizing compiler will inline all
    the iterator operations (, , etc)
  • Should be just as fast as customized code!
    (almost).

36
Achieving Greater Customization
  • Note that our sort function required that the
    ElementType in the container define an
    operatorlt().
  • What if there is no natural lt operator?
  • What if there could be more than one way to sort
    the elements?
  • Wed like to be able to override the default
    comparison and define our own.
  • Pointer to function would work, but would be WAY
    SLOW!

37
Function Objects
  • We get inlining with templates because
    instantiation is done at compile time.
  • If we want performance, we want the comparison
    function to somehow be a template parameter (not
    a function parameter).
  • What about an object type that has a known
    method?
  • In C the convention is to use a class object
    that defines the method called operator().
  • Such an object is called a function object.

38
Custom Sorting
  • template lttypename FI, typename LTFuncgt
  • void bubbleSort(FI begin, FI end, LTFunc f)
  • if (begin end) return
  • for (FI i begin i ! end i)
  • FI j i
  • FI k j
  • k
  • while (k ! end)
  • if (f(j, k)) // j less than k
  • swap(j, k)
  • j
  • k

39
Achieving the Default with Overloading
  • template lttypename Tgt
  • class default_LT
  • public
  • bool operator()(const T x, const T y)
  • return x lt y
  • template lttypename FIgt
  • void sort(FI b, FI e)
  • sortltFI, Tgt(b, e, default_LTltTgt())
  • Uh OH! How do we know what T is?

40
Conventional tags for iterators
  • Every iterator type should define the following
    nested types (usually typedefs)
  • value_type
  • iterator_category
  • (there are other types as well, but these two are
    the most useful).
  • Given an iterator FI we know that the element
    type is FIvalue_type

41
Using the nested types
  • template lttypename FIgt
  • void sort(FI b, FI e)
  • typename FIvalue_type comp_func
  • sort(b, e, comp_func) // implicit templ args
  • The typename keyword was added to the language to
    eliminate a source of ambiguity.
  • ideally, wed like the compiler to syntax check
    template definitions (before the template is
    actually instantiated).
  • FIvalue_type could be a member function, a
    static variable or a nested type. Without
    knowing what FI is, the compiler cannot syntax
    check the template.

42
Whoops! What about Pointers?
  • One of the strengths of the C std library is
    that all of the functions can be invoked on
    arrays (the Java library for example does not
    have this property).
  • We just broke this! Consider if FI is int and
    the container is an array of ints.
  • Fortunately, there is another way to determine
    the element type that will work with both
    pointers (base types) and iterators (classes).

43
Template Specialization
  • Template specialization is a very powerful
    feature that approximates overloading
  • except for classes rather than for functions.
  • Consider the problem of defining a template class
    that would return a representative value of a
    any type.
  • T x representativeltTgt()
  • For most classes, we can use the no-arg
    constructor to get a representative object.

44
General Case
  • template lttypename Tgt
  • class Representative
  • public
  • operator T(void) const
  • return T()
  • T x representativeltTgt()
  • But what about ints? Or pointers?
  • We can define specialized templates for these.

45
Specialization for int
  • / specialization for int /
  • template ltgt
  • class Representativeltintgt
  • public
  • operator int(void) const return 42
  • Note the syntax, we are not defining a new
    template, just defining a specialization of an
    existing template.
  • Could do the same thing for double, short, etc.
  • But what about the infinite number of pointer
    types!

46
Specialization for any pointer
  • Specializations can be defined for broad classes
    of template args. For example we can define a
    specialization for RepresentativeltTgt for any
    kind of T
  • template lttypename Tgt
  • class RepresentativeltTgt
  • public
  • operator T(void) const
  • void p operator new(sizeof(T))
  • new (p) T(RepresentativeltTgt())
  • return static_castltTgt(p)

47
Iterator Traits
  • Back to our iterator problem. Recall that we
    wanted to be able to determine the element type
    for any iterator argument (including base-type
    pointers).
  • In addition to requiring all iterators to define
    a nested type for value_type we will provide a
    template class (and some specializations) called
    iterator_traits

48
  • struct forward_iterator_tag
  • struct bidirectional_iterator_tag
  • struct random_access_iterator_tag
  • template lttypename Igt
  • class iterator_traits
  • public
  • typedef typename Ivalue_type value_type
  • typedef typename Iiterator_category
  • iterator_category
  • template lttypename Tgt
  • class iterator_traitsltTgt
  • public
  • typedef T value_type
  • typedef random_access_iterator_tag
    iterator_category

49
Using Iterator Traits
  • Now we can write our default sort
  • template lttypename FIgt
  • void sort(FI b, FI e)
  • typedef typename iterator_traitsltFIgtvalue_type
    T
  • default_LTltTgt comp_func
  • sort(b, e, comp_func) // implicit templ args
  • Will work for any sequence, even if FI is a
    pointer!

50
Using iterator_category
  • For some problems a faster solution may exist for
    random access iterators than for forward
    iterators.
  • We can use the iterator_category tag to
    automatically select which algorithm to use.
  • We write a wrapper function that takes the
    begin/end iterators (as normal).
  • We write two (overloaded) functions with the same
    name.
  • One takes begin, end and an object of type
    random_access_iterator_tag,
  • the other takes begin, end and an object of type
    forward_iterator_tag

51
  • template lttypename Iteratorgt
  • int length(Iterator b, Iterator e)
  • typename iterator_traitsltIteratorgtiterator_cat
    egory
  • tag
  • real_length(b, e, tag)
  • template lttypename Iteratorgt
  • int real_length(Iterator b, Iterator e,
  • random_access_iterator_tag)
  • return e - b
  • template lttypename Iteratorgt
  • int real_length(Iterator b, Iterator e,
  • forward_iterator_tag)
  • int c 0
  • while (b ! e)
  • c 1

52
Fast/Lazy Copy
  • The C programming language (and associated
    style) tends to result in making lots of copies
    of objects.
  • For container objects
  • Wastes memory
  • Wastes time
  • We only really need to copy the object to
  • avoid delete problems
  • make the objects appear independent (if one
    changes, the other does not change).

53
Lazy Copy for Containers
  • With template containers, it is sufficient to use
    reference counting for garbage determination.
  • for container C and element type T, you know that
    CltTgt ! T and thus we cant have cycles.
  • The rest of the work simply is to ensure that we
    copy at least as often as we have to.
  • e.g., at least when an object is about to be
    changed and the reference count is gt1.

54
Lazy copy for Vectors
  • Copy constructor should not actually copy
    elements
  • increment reference count and match pointers
  • Mutating functions are
  • push_back, pop_front, etc.
  • operator (non-const)
  • front, back (non-const)
  • You have your option on
  • begin/end

55
Watch the Iterators!
  • Iterators and lazy copy dont usually get along
    very well together.
  • Vectorltintgt v // initialized somehow
  • Vectorltintgtiterator p v.begin()
  • Vectorltintgt w v // lazy copy
  • p 42 // MUST NOT CHANGE w0!!!
  • w0 17
  • cout ltlt v0 ltlt endl // must print 42
  • cout ltlt p ltlt endl // must print 42
  • cout ltlt w0 ltlt endl // must print 17
Write a Comment
User Comments (0)
About PowerShow.com