generic programming and concepts that should be in c++

33
Generic programming [email protected]

Upload: anton-kolotaev

Post on 26-Jun-2015

312 views

Category:

Engineering


1 download

DESCRIPTION

My talk for ITMO students at fall 2009

TRANSCRIPT

Page 1: Generic programming and concepts that should be in C++

Generic [email protected]

Page 3: Generic programming and concepts that should be in C++

Generic Programming

• Programming paradigm for developing efficient, reusable software libraries

• Three primary tasks:• Categorize abstractions in a domain into

concepts• Implement generic algorithms based on

concepts• Build concrete models for the concepts

Page 4: Generic programming and concepts that should be in C++

Characteristics of Generic Libraries

• Reusable: able to operate on user-defined types

• Composable: able to operate on data types defined in another library

• Efficient: performance on par with non-generic, hand-coded implementations

Page 5: Generic programming and concepts that should be in C++

Generic Programming Process1. The Generic Programming process focuses on finding

commonality among similar implementations of the same algorithm, then providing suitable abstractions in the form of concepts so that a single, generic algorithm can realize many concrete implementations.

2. This process, called lifting, is repeated until the generic algorithm has reached a suitable level of abstraction, where it provides maximal reusability without sacrificing performance.

3. Dual to the lifting process is specialization, which synthesizes efficient concrete implementations for particular uses of a generic algorithm. Only by balancing the lifting and specialization processes can we ensure that the resulting generic algorithms are both reusable and efficient.

Page 6: Generic programming and concepts that should be in C++

Lifting Basic Typesint sum(int* array, int n) { int result = 0; for (int i = 0; i < n; ++i) result = result + array[i]; return result;}

float sum(float* array, int n) { float result = 0; for (int i = 0; i < n; ++i) result = result + array[i]; return result;}

template<typename T>T sum(T* array, int n) { T result = 0; for (int i = 0; i < n; ++i) result = result + array[i]; return result;}

Page 7: Generic programming and concepts that should be in C++

Lifting Basic Typesstd::string concatenate(std::string* array, int n) { std::string result = ""; for (int i = 0; i < n; ++i) result = result + array[i]; return result;}

// Requirements:// T must have a default constructor that produces the identity value// T must have an additive operator +// T must have an assignment operator// T must have a copy constructortemplate<typename T>T sum(T* array, int n) { T result = T(); for (int i = 0; i < n; ++i) result = result + array[i]; return result;}

Page 8: Generic programming and concepts that should be in C++

Lifting Containers

// Requirements:// T must have a default constructor that produces the identity // value// T must have an additive operator +// T must have an assignment operator// T must have a copy constructor// Container must have an indexing operator [] that returns a Ttemplate<typename Container, typename T>T sum(const Container& array, int n) { T result = T(); for (int i = 0; i < n; ++i) result = result + array[i]; return result;}

Page 9: Generic programming and concepts that should be in C++

Lifting Iterationtemplate<typename T>struct list_node { list_node<T>* next; T value;};

struct linked_list { list_node<T>* start;};

template<typename T>T sum(list_node<T> list, int n) { T result = 0; for (list_node<T>* current = list.start; current != NULL; current = current->next) result = result + current->value; return result;}

Page 10: Generic programming and concepts that should be in C++

Lifting Iteration// Requirements:// T must have an additive operator +// T must have an assignment operator// T must have a copy constructor// I must have an inequality operator !=// I must have a copy constructor// I must have an operation next() that moves to the next value in // the sequence// I must have an operation get() that returns the current value (of // type T).template<typename I, typename T>T sum(I start, I end, T init) { for (I current = start; current != end; current = next(current)) init = init + get(current); return init;}

Page 11: Generic programming and concepts that should be in C++

Concepts// Requirements:// T must have an equality operator ==// T must have a copy constructor// I must have an inequality operator !=// I must have a copy constructor// I must have an assignment operator// I must have an operation next() that moves to the next value in // the sequence// I must have an operation get() that returns the current value // (of type T).template<typename I, typename T>I find(I start, I end, T value) { for (I current = start; current != end; current = next(current)) if (get(current) == value) return current; return end;}

Page 12: Generic programming and concepts that should be in C++

ConceptsConcept Requirements

CopyConstructible<T> T must have a copy constructor

Assignable<T> T must have an assignment operator

Addable<T> T must have an operator+ that takes two T values and returns a T

EqualityComparable<T> T must have an operator== comparing two Ts and returning a bool.T must have an operator!= comparing two Ts and returning a bool.

Iterator<I, T> I must have an operator== comparing two Is and returning a bool.I must have an operator!= comparing two Is and returning a bool.I must have a copy constructor.I must have an assignment operator.I must have an operation next() that moves to the next value in the sequence.I must have an operation get() that returns the current value (of type T).

Page 13: Generic programming and concepts that should be in C++

Concepts// Requirements: Addable<T>, Assignable<T>, CopyConstructible<T>, // Iterator<I, T>template<typename I, typename T>T sum(I start, I end, T init);

// Requirements: EqualityComparable<T>, Assignable<T>, // CopyConstructible<T>, Iterator<I, T>template<typename I, typename T>I find(I start, I end, T value);

Page 14: Generic programming and concepts that should be in C++

Nested RequirementsConcept Requirements

Iterator<I, T> EqualityComparable<I>, CopyConstructible<I>, Assignable<I>I must have an operation next() that moves to the next value in the sequence.I must have an operation get() that returns the current value (of type T).

Page 15: Generic programming and concepts that should be in C++

Associated TypesConcept Requirements

Iterator<I> EqualityComparable<I>, CopyConstructible<I>, Assignable<I>I must have an operation next() that moves to the next value in the sequence.value_type is an associated type, accessible via iterator_traits<I>::value_typeI must have an operation get() that returns the current value (of type value_type).

// Requirements: Iterator<I>, Addable<value_type>, // Assignable<value_type>, CopyConstructible<value_type>template<typename I>typename iterator_traits<T>::value_type sum(I start, I end, typename iterator_traits<T>::value_type init) { for (I current = start; current != end; current = next(current)) init = init + get(current); return init;}

template<typename T>struct iterator_traits<T*> { typedef T value_type; };

Page 16: Generic programming and concepts that should be in C++

Concept RefinementConcept Requirements

BidirectionalIterator<I> Refines Iterator<I>I must have an operation prev() that moves to the previous value in the sequence.I must have an operation set() that sets the current value.

Page 17: Generic programming and concepts that should be in C++

Specialization// Requirements: Polygon<P>template<typename P>double circumference(P p) { double result = 0; for (int i = 0; i < num_sides(p); ++i) result += side_length(p, i); return result;}

// Requirements: EquilateralPolygon<P>template<typename P>double circumference(P p) { return num_sides(p) * side_length(p, 0);}

Page 18: Generic programming and concepts that should be in C++

Anatomy of a ConceptA concept is a set of requirements consisting of valid expressions, associated types,

invariants, and complexity guarantees. A type that satisfies the requirements is said to model the concept. A concept can extend the requirements of another concept, which is called refinement.

• Valid Expressions are C++ expressions which must compile successfully for the objects involved in the expression to be considered models of the concept.

• Associated Types are types that are related to the modeling type in that they participate in one or more of the valid expressions. Typically associated types can be accessed either through typedefs nested within a class definition for the modeling type, or they are accessed through a traits class.

• Invariants are run-time characteristics of the objects that must always be true, that is, the functions involving the objects must preserve these characteristics. The invariants often take the form of pre-conditions and post-conditions.

• Complexity Guarantees are maximum limits on how long the execution of one of the valid expressions will take, or how much of various resources its computation will use.

Page 19: Generic programming and concepts that should be in C++

Traits classesA traits class provides a way of associating information with a compile-time

entity (a type, integral constant, or address) in non-intrusive (!!!) way

template <class T>struct iterator_traits <T*>{ typedef random_access_iterator_tag iterator_category; typedef T value_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef T& reference;};

Page 20: Generic programming and concepts that should be in C++

namespace std { struct input_iterator_tag { }; struct bidirectional_iterator_tag : input_iterator_tag { }; struct random_access_iterator_tag : bidirectional_iterator_tag { };

template <class InputIterator, class Distance> void advance(InputIterator& i, Distance n) { typename iterator_traits<InputIterator>::iterator_category category; detail::advance_dispatch(i, n, category); }}

Tag Dispatching

Page 21: Generic programming and concepts that should be in C++

Tag Dispatching namespace detail { template <class InputIterator, class Distance> void advance_dispatch(InputIterator& i, Distance n, input_iterator_tag ) { while (n--) ++i; }

template <class BidirectionalIterator, class Distance> void advance_dispatch(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag ) { if (n >= 0) while (n--) ++i; else while (n++) --i; }

template <class RandomAccessIterator, class Distance> void advance_dispatch(RandomAccessIterator& i, Distance n, random_access_iterator_tag ) { i += n; }

Page 22: Generic programming and concepts that should be in C++

Fragile C++ Templatestemplate <class InputIterator, typename T>InputIterator find(InputIterator first, InputIterator beyond, const T& value){ while (first < beyond && !(*first == value)) ++first; return first;};

std::vector<int> v;find(v.begin(), v.end(), 42); // Ok

std::list<int> l;find(l.begin(), l.end(), 42); // error

Page 23: Generic programming and concepts that should be in C++

Concepts for C++: Goals

• Support for the core ideas of GP in C++• Modular type checking for C++ templates• Performance equivalence to C++ templates• Complete backward compatibility• Simplicity• C++0x

Page 24: Generic programming and concepts that should be in C++

Concepts Overview

• Concepts definitions: Specify the behaviour of types via requirements

• Where clauses: Specify constraints on template parameters in terms of concepts

• Concept maps: Specify how types meet the requirements of concepts

Page 25: Generic programming and concepts that should be in C++

Constrained Templates

• Place constraints on template parameters via a where clause

template <typename T> where LessThanComparable<T> const T& min(const T& x, const T& y){ return x < y ? x : y;}

Page 26: Generic programming and concepts that should be in C++

Concept Definitions

• Concept definitions state requirements on type parameters

concept LessThanComparable<typename T> { bool operator < (T, T);

axiom Irreflexivity(T x) { !(x < x); }

axiom Assymetry(T x, T y) { if (x < y) !(y < x); } axiom Transitivity(T x, T y, T z) { if (x < y && y < z) x < z; }}

Page 27: Generic programming and concepts that should be in C++

Member Function Requirements

concept CopyConstructable<typename T> { T::T(T const &); // copy constructor T::~T(); // desctructor}

Page 28: Generic programming and concepts that should be in C++

Iterators and Nested Requirementsconcept InputIterator <typename Iter> {

Iter& operator ++ (Iter&); Iter operator ++ (Iter&,int); bool operator == (Iter, Iter); bool operator != (Iter, Iter); ???? operator * (Iter);};

Page 29: Generic programming and concepts that should be in C++

Iterators and Nested Requirementsconcept InputIterator <typename Iter> { typename value_type; typename difference_type; where SignedIntergral<difference_type>; Iter& operator ++ (Iter&); Iter operator ++ (Iter&,int); bool operator == (Iter, Iter); bool operator != (Iter, Iter); value_type operator * (Iter);};

Page 30: Generic programming and concepts that should be in C++

Using Associated Typestemplate <class Iter, typename T> where InputIterator<Iter> && EqualityComparable<InputIterator<Iter>::value_type, T> Iter find(Iter first, Iter beyond, const T& value){ while (first != beyond && !(*first == value)) ++first; return first;};

Page 31: Generic programming and concepts that should be in C++

Concept Maps

• Concept maps satisfy concept constraints

bool contains(int const * array, int n, int value) { return find(array, array + n, value) != array + n;}

template <class T>concept_map InputIterator<T*> { typedef T value_type; typedef ptrdiff_t difference_type;}

Page 32: Generic programming and concepts that should be in C++

Concept Refinement// A bidirectional iterator can move backwardconcept BidirectionalIterator<typename Iter> : InputIterator<Iter>{ Iter& operator -- (Iter&); Iter operator -- (Iter&, int);}

// A random access iterator can jump aroundconcept RandomIterator<typename Iter> : BidirectionalIterator<Iter>{ Iter operator + (Iter, difference_type); // ...}

Page 33: Generic programming and concepts that should be in C++

Concept-based overloadingtemplate <InputIterator Iter> void advance(Iter & x, Iter::difference_type n){ for (; n > 0; --n) ++x; // O(n)}

template <RandomAccessIterator Iter> void advance(Iter & x, Iter::difference_type n){ x += n; // O(1)}