C++ Blog

C++ Concept Part 3

Posted in c++0x, templates by Umesh Sirsiwal on January 25, 2009

This is the last post in series of posts on C++0x concepts. In this we will look at C++0x concepts. The C++0x standard for concepts is almost identical to ConceptC++. The syntax are almost identical. Significant parts of ConceptC++ have become part of the draft standard.

One of the things we have not looked at the definition of concept map. In the previous example of sort, the template takes ForwardIterator as an argument. The article did not discuss what makes a forward iterator. Can we use an int* as a forard iterator? We have also said that ForwardIterator must has value_type argument. int* does not have a member value_type. But we want to pass int* as parameter to sort so that for example, we can sort an array of integers.

C++0x provides a way to solve this problem called concept_map. Using concept_map we can define what constitute ForwardIterator.

template ;
	concept_map ForwardIterator {
                                    // T*'s value_type is T
		typedef T value_type;
	};

Using above syntax we are saying that any T”*’s value_type is T. This removes need to wrap int* in another container.

In all C++0x concept provides solution for one of the biggest ongoing frustration with generic programming with C++. It will provide early and clear compilation errors for template users and authors both and bind them in template parameter contract.

Other C++0x sources on the web

Advertisements

C++0x Concepts Contd..

Posted in c++0x, templates by Umesh Sirsiwal on January 18, 2009

In previous article we had discussed BCCL. This post continues to discuss Concept with ConceptC++. The ConceptC++ was the playground for language level Concept ideas which eventually became part of currently proposed C++0x standard. In this post we take in-depth view to ConceptC++. These extensions were implemented by modifying GCC.

ConceptC++

Unlike the Boost Concept Check Library (BCCL), which is implemented purely using standard C++ constructs, these extensions are implemented by extending C++ language. By changing the C++ definition, it is possible to do things which was not possible otherwise. In particular, remember that with BCCL, it was not possible to make template parameter restriction part of template or function definition. With ConceptC++, this becomes now possible. In addition to enforcing restriction on instantiation of template, ConceptC++ also imposes restrictions on template implementation and makes sure that it only uses template parameter’s facility defined in the contract.

To understand ConceptC++, let us consider sum template which adds two parameters and returns the value:

template <typename T>
T sum(T a, T b){
   return a+b;
}

With Concept C++ we can rewrite this to:

template <CopyConstructable T>
T sum(T a, T b){
   return a+b;
}

This says T must be copy constructable. If we compile this template using ConceptC++ (even without any instantiation) we get the following error:

error: no match for 'operator+' in 'a+b'

That is because the template definition did not require T to implement operator+ but is trying to use it. This forces template signature to accurately define the template signature as:

template <CopyConstructable T>
  requires Addable<T>
T sum(T a, T b){
   return a+b;
}

In other words ConceptC++ puts restrictions on both template instantiation and template definition and binds them in a contract with respect to template parameter. This is what function, class or template definition, etc. are supposed to be.

We have been using magic words like CopyConstructable, Addable, etc. What are they? Are they new language features or are provided by library? What if we wanted to implement our own custom concepts? How will we go about that.

The C++ language addition provides capability to define new concepts while the library provides set of standard concepts. It is rather easy to build your own concept. For example let us look at definition of Addable concept:

auto concept Addable<typename T> {
  T operator+(T x, T y);
};

How simple can that be? The above definition is saying that the Addable concept implies that the template parameter must implement operator+ which takes two parameters of type T and returns T.

With this background, let us modify out example sort template. In case of sort, the template parameters are iterators and value pointed to by the iterator must implement less than operator. With ConceptC++ you can specify these restrictions as follows:

#include <concepts>
template<std::ForwardIterator T>
    requires LessThanComparable<T::value_type>
void sort(T b, T e)
{
  ....
}

As you can see ConceptC++ provides natural way of definining restrictions on template parameters allowing compilers to perform early error checking.


Performance


The ConceptC++ implements its functionality by reducing compile speed. However, just like BCCL the ConceptC++ does not incur any run time overhead.




	

C++0x "Concepts"

Posted in c++0x by Umesh Sirsiwal on January 17, 2009

It is generally agreed that “Concept” is the biggest addition to C++ during C++0x (e.g. look @ Herb Shutter’s blog). The subject is large and I possibly cannot explain it in a single post. This post provides high level concept behind C++0x Concept.

Any person who has ever used C++ templates knows that compiler error messages when using templates are pain to understand. The problem stands from the fact that there is no easy way for template developers to define constraints on template parameters. For example let us consider the following example:

#include <vector>
#include <complex>
#include <algorithm>

int main()
{
     std::vector<std::complex<float> > v;
     std::stable_sort(v.begin(), v.end());
}

On my machine running GCC 4.2.1 it produces the following error:

/usr/include/c++/4.2.1/bits/stl_algo.h: In function âvoid std::__insertion_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â:

/usr/include/c++/4.2.1/bits/stl_algo.h:3176:   instantiated from âvoid std::__inplace_stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3892:   instantiated from âvoid std::stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:11:   instantiated from here

/usr/include/c++/4.2.1/bits/stl_algo.h:2356: error: no match for âoperator<â in â__val < __first. __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = std::complex<float>*, _Container = std::vector<std::complex<float>, std::allocator<std::complex<float> > >]()â

/usr/include/c++/4.2.1/bits/stl_algo.h: In function âvoid std::__merge_without_buffer(_BidirectionalIterator, _BidirectionalIterator, _BidirectionalIterator, _Distance, _Distance) [with _BidirectionalIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >, _Distance = int]â:

/usr/include/c++/4.2.1/bits/stl_algo.h:3182:   instantiated from âvoid std::__inplace_stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3892:   instantiated from âvoid std::stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:11:   instantiated from here

/usr/include/c++/4.2.1/bits/stl_algo.h:3082: error: no match for âoperator<â in â__middle. __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = std::complex<float>*, _Container = std::vector<std::complex<float>, std::allocator<std::complex<float> > >]() < __first. __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = std::complex<float>*, _Container = std::vector<std::complex<float>, std::allocator<std::complex<float> > >]()â

/usr/include/c++/4.2.1/bits/stl_algo.h: In function âvoid std::__unguarded_linear_insert(_RandomAccessIterator, _Tp) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >, _Tp = std::complex<float>]â:

/usr/include/c++/4.2.1/bits/stl_algo.h:2362:   instantiated from âvoid std::__insertion_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3176:   instantiated from âvoid std::__inplace_stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3892:   instantiated from âvoid std::stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:11:   instantiated from here

/usr/include/c++/4.2.1/bits/stl_algo.h:2309: error: no match for âoperator<â in â__val < __next. __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = std::complex<float>*, _Container = std::vector<std::complex<float>, std::allocator<std::complex<float> > >]()â

/usr/include/c++/4.2.1/bits/stl_algo.h: In function â_ForwardIterator std::lower_bound(_ForwardIterator, _ForwardIterator, const _Tp&) [with _ForwardIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >, _Tp = std::complex<float>]â:

/usr/include/c++/4.2.1/bits/stl_algo.h:3094:   instantiated from âvoid std::__merge_without_buffer(_BidirectionalIterator, _BidirectionalIterator, _BidirectionalIterator, _Distance, _Distance) [with _BidirectionalIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >, _Distance = int]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3182:   instantiated from âvoid std::__inplace_stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3892:   instantiated from âvoid std::stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:11:   instantiated from here

/usr/include/c++/4.2.1/bits/stl_algo.h:3252: error: no match for âoperator<â in â* __first2 < * __first1â

Experienced C++ programmers may be able to figure out this error message however for most users it is extremely difficult. The error message is long and does not provide any insight in to cause of the error. The basic error is that  std:complex<float> does not model LessThanComparable which is required by std:sort.  In C++ there is no easy way for template developers to provide requirements on their parameters. Hence, compilers are not capable of testing the parameter errors early and provide meaningful error message. This C++ deficiency has significantly affected wider adaptation of templates and has resulted in development various methods of providing template parameter checks. These alternatives include both Language based and library based. C++0x Concepts will provide language level facility for error early error detection and meaningful error generation for template parameters.

It will probably take some time before MSVC and G++ support Concepts in their mainline languages. Till such time, existing alternatives may provide you with short term solutions.

Boost Concept Check Library

The Boost Concept Checking Library provides a library level concepts check facility without any language changes. Also the mechanism does not incur any run-time overhead. The checks are completely implemented at compile time. According to BCCL, it provides:

  • A mechanism for inserting compile-time checks on template parameters at their point of use.
  • A framework for specifying concept requirements though concept checking classes.
  • A mechanism for verifying that concept requirements cover the template.
  • A suite of concept checking classes and archetype classes that match the concept requirements in the C++ Standard Library.
  • An alternative to the use of traits classes for accessing associated types that mirrors the syntax proposed for the next C++ standard.

With BCCL the std::stable_sort can be written as:

template<typename T>
void sort(T b, T e)
{
  function_requires< LessThanComparableConcept<T> >();
  typedef typename
     std::iterator_traits<T>::value_type value_type;
  function_requires
     <LessThanComparableConcept<value_type> >();
  ....
}

With this change the error message changes to:

/usr/include/boost/concept_check.hpp: In member function âvoid boost::LessThanComparableConcept<TT>::constraints() [with TT = std::complex<float>]â:

/usr/include/boost/concept_check.hpp:48:   instantiated from âvoid boost::function_requires(boost::mpl::identity<T>*) [with Concept = boost::LessThanComparableConcept<std::complex<float> >]â

t.cc:13:   instantiated from âvoid mysort(T, T) [with T = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:21:   instantiated from here

/usr/include/boost/concept_check.hpp:314: error: no match for âoperator<â in â((boost::LessThanComparableConcept<std::complex<float> >*)this)->boost::LessThanComparableConcept<std::complex<float> >::a < ((boost::LessThanComparableConcept<std::complex<float> >*)this)->boost::LessThanComparableConcept<std::complex<float> >::bâ

/usr/include/c++/4.2.1/bits/stl_algo.h: In function âvoid std::__insertion_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â:

/usr/include/c++/4.2.1/bits/stl_algo.h:3176:   instantiated from âvoid std::__inplace_stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3892:   instantiated from âvoid std::stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:15:   instantiated from âvoid mysort(T, T) [with T = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:21:   instantiated from here

/usr/include/c++/4.2.1/bits/stl_algo.h:2356: error: no match for âoperator<â in â__val < __first. __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = std::complex<float>*, _Container = std::vector<std::complex<float>, std::allocator<std::complex<float> > >]()â

/usr/include/c++/4.2.1/bits/stl_algo.h: In function âvoid std::__merge_without_buffer(_BidirectionalIterator, _BidirectionalIterator, _BidirectionalIterator, _Distance, _Distance) [with _BidirectionalIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >, _Distance = int]â:

/usr/include/c++/4.2.1/bits/stl_algo.h:3182:   instantiated from âvoid std::__inplace_stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3892:   instantiated from âvoid std::stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:15:   instantiated from âvoid mysort(T, T) [with T = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:21:   instantiated from here

/usr/include/c++/4.2.1/bits/stl_algo.h:3082: error: no match for âoperator<â in â__middle. __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = std::complex<float>*, _Container = std::vector<std::complex<float>, std::allocator<std::complex<float> > >]() < __first. __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = std::complex<float>*, _Container = std::vector<std::complex<float>, std::allocator<std::complex<float> > >]()â

/usr/include/c++/4.2.1/bits/stl_algo.h: In function âvoid std::__unguarded_linear_insert(_RandomAccessIterator, _Tp) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >, _Tp = std::complex<float>]â:

/usr/include/c++/4.2.1/bits/stl_algo.h:2362:   instantiated from âvoid std::__insertion_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3176:   instantiated from âvoid std::__inplace_stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3892:   instantiated from âvoid std::stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:15:   instantiated from âvoid mysort(T, T) [with T = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:21:   instantiated from here

/usr/include/c++/4.2.1/bits/stl_algo.h:2309: error: no match for âoperator<â in â__val < __next. __gnu_cxx::__normal_iterator<_Iterator, _Container>::operator* [with _Iterator = std::complex<float>*, _Container = std::vector<std::complex<float>, std::allocator<std::complex<float> > >]()â

/usr/include/c++/4.2.1/bits/stl_algo.h: In function â_ForwardIterator std::lower_bound(_ForwardIterator, _ForwardIterator, const _Tp&) [with _ForwardIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >, _Tp = std::complex<float>]â:

/usr/include/c++/4.2.1/bits/stl_algo.h:3094:   instantiated from âvoid std::__merge_without_buffer(_BidirectionalIterator, _BidirectionalIterator, _BidirectionalIterator, _Distance, _Distance) [with _BidirectionalIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >, _Distance = int]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3182:   instantiated from âvoid std::__inplace_stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

/usr/include/c++/4.2.1/bits/stl_algo.h:3892:   instantiated from âvoid std::stable_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:15:   instantiated from âvoid mysort(T, T) [with T = __gnu_cxx::__normal_iterator<std::complex<float>*, std::vector<std::complex<float>, std::allocator<std::complex<float> > > >]â

t.cc:21:   instantiated from here

….
….

/usr/include/c++/4.2.1/bits/stl_algo.h:3252: error: no match for âoperator<â in â* __first2 < * __first1â

Clearly a better error message (still a little longer than what we want it to be).

Although, BCCL provides better error message. The biggest drawback of the BCCL is that the restriction on the template parameter is not part of template prototype and hence not easily discoverable by algorithm user.  Language level support is needed to support that. The ConceptC++ was an experimental implementation of language level support. Significant parts of C++0x is derived from ConceptC++ and boost experimantation. I will discuss these changes in future posts.

Java Style Dynamic Proxy in C++

Posted in c++0x by Umesh Sirsiwal on January 9, 2009

This post defines C++ template based implementation for C++ proxy.

I recently came across Java’s proxy object. That is some cool technology. Using this one can wrap one object in another object in a completely typesafe way. From user’s point of view, the proxy object looks something like:

ProxiedObjectInterface obj = (ProxiedObjectInterface)ProxyCreator.newInstance( proxiedObject );

More details on proxy object are available here.

Since the interface exposed is of the proxied type, all calls to obj are typsef and can be checked by compiler. In java ProxyCreator class does not need to know anything about proxied object. This makes it very powerful.

I have been wondering if there is any easy way to create a similar facility in C++. I cannot think of one right away. I know we can do some cool things with template meta-programming. What will that be?

Tagged with: , , ,

Static Assert in C++0x

Posted in c++0x by Umesh Sirsiwal on January 5, 2009

This is post in the C++ Reference series on this site.

All the C++ programmers have used static asserts some have used Loki version, others have used Boost version or created their own. However, in all cases the error messages generated by Static Assert are incomplete. A language level support is needed to correctly support Static Assert.

The C++0x adds Static Assert to solve this specific issue. The synatx for static assert is something like:

static_assert(sizeof(int) == 4, "Size of int is not 4 bytes.") ;

On any machine where the sizeof(int) is not 4, the compilers should correctly generate error message “Size of int is not 4 bytes”. Sweet.

Null Pointer and C++

Posted in c++0x by Umesh Sirsiwal on January 5, 2009

This is part of C++ Reference series.

C++ is a strongly typed language except when it comes to null pointers. 0 is used both to represent a null pointer and integer value zero. This can cause nasty issues since 0’s type cannot be determined. C++0x adds a new reserved word nullptr to solve this issue.

nullptr is zero pointer that can be converted to any pointer type.

C++0x Auto Types

Posted in c++0x by Umesh Sirsiwal on January 5, 2009

This is part of C++ Reference series.

Several OO languages including SmallTalk, Python, Ruby, etc have a way to creating new variables with the type of existing variables. In C++ to create a variable, the program always had to know the type.

Starting C++0x this restriction goes away. C++0x adds support for auto-types. For example it is now possible to code:

auto a = b->c;
auto a(b->c);

Both these statements will create a variable ‘a’ with type b->c and assign it value of b->c.

No need to  know the type of b->c. Why do we need autotypes. We thought type safety is important. Let us consider the following code segment:
for (std::vector<int>::iterator it= vi.begin(); it!=vi.end(); ++it)

with auto types this simplifies to:
for(auto vi::iterator it = vi.begin(); it  != vi.end; ++it)

Significantly less verbose and more maintainable code. We can change type of vi from vector to list, deque, etc. without changing type of every iterator.

The code no longer cares about type of vi. It just declares an iterator of the type vi::iterator.

In addition, for memory allocation it is now possible to say:

auto* a = new auto(b->c); // new object of the type b->c and assign it to a new variable of type pointer to b->c.

More interesting syntax:

auto p = std::string("xxx"); // p has type of std::string
auto p[] = "xxx"; // p has type of char[4]
auto* p = "xxx"; // p has type char*
auto a = 10; // a has type integer

References:
http://std.dkuug.dk/JTC1/SC22/WG21/docs/papers/2003/n1527.pdf

http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=219