Concepts Jyrki Katajainen (University of Copenhagen) Sources: - - PowerPoint PPT Presentation

concepts
SMART_READER_LITE
LIVE PREVIEW

Concepts Jyrki Katajainen (University of Copenhagen) Sources: - - PowerPoint PPT Presentation

Concepts Jyrki Katajainen (University of Copenhagen) Sources: Perform web-search with term ConceptC ++ Course home page: http://www.diku.dk/forskning/ performance-engineering/ Generic-programming/ c Performance Engineering Laboratory


slide-1
SLIDE 1

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (1)

Concepts

Jyrki Katajainen (University of Copenhagen)

Sources:

  • Perform web-search with term ConceptC++

Course home page: http://www.diku.dk/forskning/ performance-engineering/ Generic-programming/

slide-2
SLIDE 2

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (2)

Polymorphism

The word polymorphism means “the ability to have many forms”. Parametric polymorphism: C++ templates Inclusion polymorphism: C++ virtual functions Overloading: C++ function overloading including partial specializa- tion Coercion: C++ built-in or user-defined conversion operators or con- structors to coercion.

slide-3
SLIDE 3

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (3)

Bounded polymorphism

Bounded parametric polymorphism (or constrained genericity) means that we can specify some constraints on type parameters. In C++, there is no way to specify constrains on type parameters, but many clever tricks and workarounds exist to support generic program- ming (including type mappings, tag dispatching, and SFINAE) There are two approaches to specify constraints on type parameters:

  • 1. use as a type definition

template <cphstl::HasLess T> class point { // ... }

  • 2. list as a requirement

template <typename T> requires cphstl::HasLess<T> class point { // ... }

slide-4
SLIDE 4

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (4)

Problems with C++ templates

It is theoretically interesting that the template level of C++ has the power of a Turing machine, but template meta-programming has its problems, particularly in the areas of

  • error reporting,
  • debugging,
  • code readability,
  • code maintainability,
  • separate compilation,
  • compilation speed,
  • internal capacity and robust-

ness of compilers, and

  • portability.

Most problems seem to be related to unbounded parametric poly- morphism.

slide-5
SLIDE 5

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (5)

Compilation with g++

1 #include <list> 2 #include <algorithm> 3 4 int main() { 5

std::list<int> l;

6

std::sort(l.begin(), l.end());

7 }

kand-1> g++ -Wall -pedantic list-sort.c++ /usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/g++-v4/bits/stl_algo.h: In ← ֓ function ’voidstd::sort(_RandomAccessIterator,_RandomAccessIterator)[ ← ֓ with_RandomAccessIterator=std::_List_iterator<int>]’: list-sort.c++:6: instantiated from here /usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/g++-v4/bits/stl_algo.h:2713: ← ֓ error: no match for ’operator-’ in ’__last-__first’ /usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/g++-v4/bits/stl_algo.h: In ← ֓ function ’voidstd::__final_insertion_sort(_RandomAccessIterator, ← ֓ _RandomAccessIterator)[with_RandomAccessIterator=std::_List_iterator< ← ֓ int>]’: ...

slide-6
SLIDE 6

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (6)

Compilation with conceptg++

1 #include <list> 2 #include <algorithm> 3 4 int main() { 5

std::list<int> l;

6

std::sort(l.begin(), l.end());

7 }

kand-1> conceptg++ -Wall -pedantic list-sort.c++ list-sort.c++: In function ’intmain()’: list-sort.c++:6: error: no matching function for call to ’sort(std:: ← ֓ _List_iterator<int>,std::_List_iterator<int>)’ /usr/local/lib/gcc/i686-pc-linux-gnu/4.3.0/../../../../include/c++/4.3.0/ ← ֓ bits/stl_algo.h:2872: note: candidates are: void std::sort(_Iter, _Iter) ← ֓ [with _Iter = std::_List_iterator<int>] <where clause> list-sort.c++:6: note: no concept map for requirement ’std:: ← ֓ MutableRandomAccessIterator<std::_List_iterator<int>>’

Unfortunately, the compilation is noticeably slower. Why?

slide-7
SLIDE 7

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (7)

Four definitions

“A concept is a set of requirements [on types] bundled together under a single name.” [Gregor 2006] “a type system—called concepts—for C++ types and values that can be used for template arguments” [Reis & Stroustrup 2006] “concepts are compile-time predicates on types and values (e.g. in- tegral constant values). They can be combined with the usual logical

  • perators (and, or, not).” [Reis & Stroustrup 2006]

“Everybody’s first idea for [defining the predicates] is to specify a concept as a set of operations” [Reis & Stroustrup 2006]

slide-8
SLIDE 8

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (8)

Requirements on parameters

“The fundamental problem is that a template definition is not (by itself) a good specification of its requirements on its parameters. We need to make those requirements explicit and less ad hoc than the expression of an algorithm. ’Concepts’ are such requirements.” [Reis & Stroustrup 2006] Requirements for classes

  • typedefs defined
  • member functions defined
  • attributes defined (cannot be

expressed in Concept C++)

  • member classes defined

Requirements for functions

  • parameter types
  • return types
  • functions relied on
  • types relied on
slide-9
SLIDE 9

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (9)

Old style

template <typename R> void stable_sort(R a, R z);

Requires

  • R is a model of random-access iterator.
  • R is mutable.
  • R’s value type is strict-weakly comparable.

. . . Complexity Let N be z−a. The worst-case behaviour is O(N(lg N)2) if no auxiliary memory is available, and O(N lg N) if a large enough auxiliary memory buffer is available.

slide-10
SLIDE 10

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (10)

New style

#include <concepts> // defines std concepts #include <iterator> // defines std iterator concepts template <std::MutableRandomAccessIterator R> requires std::LessThanComparable<R::value_type> void stable_sort(R a, R z);

Preconditions

dynamic_assert(z - a ≥ 0);

Postconditions

dynamic_assert(chpstl::is_sorted(a, z));

. . . Complexity requirements Let N be z − a.. . . O(N(lg N)2). . .

slide-11
SLIDE 11

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (11)

Signatures

Signatures permit conversions of the argument and result types.

auto concept HasLess<typename T, typename U = T> { bool operator<(T const&, U const&); };

The declaration of operator< requires the existence of a < operator— either built in, as a free function, or as a member function—that can be passed two values convertible to type T and that returns a value convertible to bool. The auto preceding concept means that this is an implicit concept, for which we never have to write a model declaration.

slide-12
SLIDE 12

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (12)

Default implementations

Default implementations serve the same purpose as the comparison

  • perators in the std::rel ops namespace, but without its problems.

1 auto concept LessThanComparable<typename T> : HasLess<T> { 2

bool operator>(T const& a, T const& b) {

3

return b < a;

4

}

5 6

bool operator≤(T const& a, T const& b) {

7

return !(b < a);

8

}

9 10

bool operator≥(T const& a, T const& b) {

11

return !(a < b);

12

}

13 ... 14 };

slide-13
SLIDE 13

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (13)

Semantic concepts

1 auto concept LessThanComparable<typename T> : HasLess<T> { 2 ... 3

axiom Consistency(T a, T b) {

4

(a > b) ≡ (b < a);

5

(a ≤ b) ≡ !(b < a);

6

(a ≥ b) ≡ !(a < b);

7

}

8 9

axiom Irreflexivity(T a) {

10

(a < a) ≡ false;

11

}

12 13

axiom Antisymmetry(T a, T b) {

14

if (a < b)

15

(b < a) ≡ false;

16

}

17

axiom Transitivity(T a, T b, T c) {

18

if (a < b && b < c)

19

(a < c) ≡ true;

20

}

21 22

axiom TransitivityOf≡(T a, T b, T c) {

23

if (!(a < b) && !(b < a) &&

24

!(b < c) && !(c < b))

25

(!(a < c) && !(c < a)) ≡true;

26

}

27 };

slide-14
SLIDE 14

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (14)

Associated types

Associated types are represented as nested types within the concept; they replace traits and permit checking of template definitions.

concept IteratorAssociatedTypes<typename X> { typename value_type = X::value_type; typename difference_type = X::difference_type; typename reference = X::reference; typename pointer = X::pointer; };

If a model does not specify a type definition for an associated type, then the model uses the default.

slide-15
SLIDE 15

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (15)

Concept maps

A model declaration illustrates how a set of types will model a particular concept.

template <typename T> concept_map ForwardIterator<T*> { typedef T value_type; typedef std::size_t difference_type; typedef T& reference; typedef T* pointer; }

Each model must meet all of the requirements in the concept.

slide-16
SLIDE 16

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (16)

Some standard concepts

1 auto concept Assignable<typename T, typename U = T> { 2

typename result_type;

3

result_type T::operator=(U const&);

4 }; 5 6 auto concept Convertible<typename T, typename U> { 7

  • perator U(T const&);

8 }; 9 10 template <typename T> 11 concept_map Convertible<T, T> { 12 }; 13 14 template<typename T> 15 concept_map Convertible<T, T&> { 16 }; 17 18 template <typename T> 19 concept_map Convertible<T, T const&> { 20 };

To see more, read the concept web in the documentation of Concept C++.

slide-17
SLIDE 17

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (17)

Refinement

1 concept InputIterator<typename X> 2

: IteratorAssociatedTypes<X>, CopyConstructible<X>, EqualityComparable<X> {

3

requires

4

Assignable<X>,

5

SameType<Assignable<X>::result_type, X&>,

6

SignedIntegral<difference_type>,

7

Convertible<reference, value_type>,

8

Convertible<pointer, value_type const*>;

9 10

typename postincrement_result;

11

requires

12

Dereferenceable<postincrement_result>,

13

Convertible<Dereferenceable<postincrement_result>::reference, value_type>;

14 15

pointer operator→(X);

16

X& operator++(X&);

17

postincrement_result operator++(X&, int);

18

reference operator*(X);

19 };

slide-18
SLIDE 18

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (18)

Iterator concepts in pairs

1 concept ForwardIterator<typename X> 2

: InputIterator<X>, DefaultConstructible<X> {

3

requires

4

Convertible<reference, const value_type&>,

5

Convertible<postincrement_result, const X&>;

6 }; 7 8 concept MutableForwardIterator<typename X> 9

: ForwardIterator<X>, BasicOutputIterator<X> {

10

requires

11

SameType<reference, value_type&>,

12

SameType<pointer, value_type*>;

13 };

slide-19
SLIDE 19

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (19)

Concept-based overloading

1 template <std::InputIterator Iter> 2 void advance(Iter& i, typename Iter::difference_type n) { 3

while (n ≡ 0) {

4

++i; --n;

5

}

6 } 7 8 template <std::BidirectionalIterator Iter> 9 void advance(Iter& i, typename Iter::difference_type n) { 10

while (n > 0) {

11

++i; --n;

12

}

13

while (n < 0) {

14

  • -i; ++n;

15

}

16 } 17 18 template <std::RandomAccessIterator Iter> 19 void advance(Iter& i, typename Iter::difference_type n) { 20

i += n;

21 }

slide-20
SLIDE 20

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (20)

Example: std::fill n with cphstl extensions

template<typename OutputIterator, typename Size, typename T> void fill_n(OutputIterator first, Size n, V const& value);

Requires Type V is assignable and Size is convertible to an integral type. Effects Assigns value through all the iterators in the range [first, first + n). Complexity Exactly n assignments.

slide-21
SLIDE 21

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (21)

First attempt

1 template <typename OutputIterator, typename Size, typename V> 2 void 3 fill_n(OutputIterator first, Size n, V const& value) { 4

while (n ≡ 0) {

5

*first = value;

6

++first;

7

  • -n;

8

}

9 } 10 11 template <typename Container, typename Size, typename V> 12 void 13 fill_n(Container& C, Size n, V const& value) { 14

cphstl::fill_n(C.begin(), n, value);

15 }

kand-1> g++ ... test.c++: In function ’intmain()’: test.c++:22: error: call of overloaded ’fill_n(int[10],constunsignedint ← ֓ &,int)’ is ambiguous

slide-22
SLIDE 22

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (22)

Adding concepts to the basic version

1 template <typename I, typename S, typename V> 2 requires 3

std::OutputIterator<I, V>,

4

std::Convertible<S, std::size t>

5 void 6 fill_n(I first, S n, V const& value) { 7

std::size t k(n);

8

while (k ≡ 0) {

9

*first = value;

10

++first;

11

  • -k;

12

}

13 }

kand-1> conceptg++ -include bits/stdc++.h ... test.c++ ./a.out

slide-23
SLIDE 23

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (23)

Convertible to an integral failed

1 concept Convertible2Integral<typename S> { 2

typename integral_type;

3

requires

4

std::Integral<integral_type>,

5

std::Convertible<S, integral_type>;

6 } 7 8 template <typename I, typename S, typename V> 9 requires 10

std::OutputIterator<I, V>,

11

cphstl::Convertible2Integral<S>

12 void 13 fill_n(I first, S n, V const& value) { 14

while (n ≡ 0) {

15

*first = value;

16

++first;

17

  • -n;

18

}

19 }

error: no match for ’operator--’ in ’--n’

slide-24
SLIDE 24

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (24)

Another failure

1 template <typename I, typename S, typename V> 2 requires 3

std::OutputIterator<I, V>,

4

cphstl::Convertible2Integral<S>

5 void 6 fill_n(I first, S n, V const& value) { 7

std::size_t k(n);

8

while (k ≡ 0) {

9

*first = value;

10

++first;

11

  • -k;

12

}

13 } 26 cphstl::list<int>::iterator it = v.begin(); 27 cphstl::fill_n(it, 1, 233);

test.c++:27: error: no matching function for call to ’fill_n(std:: ← ֓ _List_iterator<int>&,int,int)’

slide-25
SLIDE 25

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (25)

Design questions

  • Should concept-checking match exactly the concepts used or can
  • ne allow over-specification?
  • Iterators appear in pairs: non-mutable iterators and mutable ite-
  • rators. Should ranges be handled identically?
  • Should ranges be categorized as iterators: input ranges, output

ranges, forward ranges, etc.?

  • Should the same atomization be done for containers?
slide-26
SLIDE 26

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (26)

Adding ranges

1 concept Range<typename R> { 2

typename iterator = R::iterator;

3

typename const_iterator = R:: ← ֓ const_iterator;

4

typename container_type = R:: ← ֓ container_type;

5 6

R::R(container_type&);

7

requires

8

std::CopyConstructible<R>,

9

std::Assignable<R>;

10 11

iterator begin(R&);

12

const_iterator begin(R const&);

13

iterator end(R&);

14

const_iterator end(R const&);

15 }; 1 template <typename C> 2 class range { 3 public: 4

typedef typename C::iterator ← ֓ iterator;

5

typedef typename C::const_iterator ← ֓ const_iterator;

6

typedef C container_type;

7 8

explicit range(C&);

9

range(range const&);

10

~range();

11

range& operator=(range const&);

12 13

iterator begin();

14

const_iterator begin() const;

15

iterator end();

16

const_iterator end() const;

17 ... 18 };

slide-27
SLIDE 27

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (27)

More compilation problems

22 template <typename R, typename S, typename V> 23 requires 24

cphstl::Range<R>,

25

std::OutputIterator<R::iterator, V>,

26

std::Convertible<S, std::size_t>

27 void 28 fill_n(R& r, S n, V const& value) { 29

cphstl::fill_n(r.begin(), n, value);

30 }

fill_n.conceptc++:29: error: ’structR’ has no member named ’begin’

After the change: iterator begin(R&); ==> iterator R::begin();

test.c++:31: error: no matching function for call to ’fill_n(cphstl::range< ← ֓ cphstl::list<int,std::allocator<int>,std::list<int,std::allocator<int> ← ֓ >>>&,size_t,int)’

After commenting out the line: std::OutputIterator<R::iterator, V>,

fill_n.conceptc++:29: error: no matching function for call to ’fill_n(cphstl ← ֓ ::Range<R>::iterator&,S&,constV&)’

slide-28
SLIDE 28

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (28)

This time I had to give up

error: no matching function for call to ’fill_n(std::list<int,std:: ← ֓ allocator<int>>&,int,int)’ error: call of overloaded ’fill_n(cphstl::Range<R>::iterator&,S&,constV&) ← ֓ ’ is ambiguous error: no match for ’operator*’ in ’*first’ error: ’structC’ has no member named ’begin’ error: incomplete type ’I’ used in nested name specifier

How could I trace the actions done by my compiler?

slide-29
SLIDE 29

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (29)

Levels of learning

I: Use concepts written by others. II: Write concepts to be used by yourself. III: Develop concepts to be used by others. IV: Develop concepts to be used by millions of others.

slide-30
SLIDE 30

c

Performance Engineering Laboratory

Generic programming and library development, 3 June 2008 (30)

Pros and cons of constrained genericity

+ improved error messages + debugging easier for library authors + explicit descriptions

  • f

the import interfaces + new opportunities for over- loading + separate compilation pos- sible + improved static type check- ing + lower barrier to novices – more to learn – more to type – duplication of the interface information – flexibility of lazy type check- ing lost – possibility for over-specifica- tion