Collection Traits


Introduction

This library makes it possible to treat different types as if they have implemented a subset of the container requirements (see §23.1of the C++ standard). Formally, that subset is defined by the CollectionConcept. The subset deals mostly with iterator returning functions and nested typedefs. The main goal is to treat built-in arrays, standard containers, pairs of iterators and some iterators uniformly. Formally, this library is an implementation of the ExternalCollectionConcept (see also this explanation of External Concepts ).

The main advantages are

Below are given a small example (the complete example can be found here ):

       //
       // Example: extracting bounds in generic algorithms
       //                
                       
       template< typename ExternalCollection, typename T >
       inline typename boost::iterator_of<ExternalCollection>::type
       find( ExternalCollection& c, const T& value )
       {
           return std::find( boost::begin( c ), boost::end( c ), value );
       }

       template< typename ExternalCollection, typename T >
       inline typename boost::const_iterator_of<ExternalCollection>::type 
       find( const ExternalCollection& c, const T& value )
       {
           return std::find( boost::begin( c ), boost::end( c ), value );
       }
                       
       // 
       // replace first value and return its index
       //                                
       template< typename EC, typename T >
       inline typename boost::size_type_of< EC >::type
       my_generic_replace( EC& c, const T& value, const T& replacement )
       {
           typename boost::const_iterator_of<EC>::type found = find( c, value );
           *found = replacement;
           return std::distance( boost::begin( c ), found );
       }                  
                       
       // 
       // usage
       //                                
       std::vector<int>              my_vector;
       typedef vector<int>::iterator iterator;
       std::pair<iterator,iterator>  my_view( my_vector.begin(), my_vector.begin() + N );
       char str[] = "a string";
       // ...
       std::cout << my_generic_replace( my_vector, 4, 2 )
                 << my_generic_replace( my_view, 4, 2 )
                 << my_generic_replace( str, 'a', 'b' );
       
By using the free-standing functions and type-generators, the code automatically works for all the types supported by this library. Notice that we have to provide two version of find() since we cannot forward a non-const rvalue with reference arguments (see this article about The Forwarding Problem ).


Reference

#include <boost/collection_traits.hpp>

Five types of objects are currently supported by the library:

It is worth noticing that some functionality requires partial template specialization, in particular, full array support does (remark: this is a very small problem since one would use boost::array<> anyway). Also note that arrays and pointers of char or whar_t are treated special because of their use in string algorithms.

ExternalCollectionConcept

The concept is defined by the type-generators and the functions below. Even though these functions are defined in namespace boost, there is no such general requirement, that is, if one wants to extend the list of supported types, it can be done in any namespace.

Synopsis

    namespace boost 
    {
        //             
        // type generators
        //              
        
        template< typename EC >                                     
        struct value_type_of
        {
            typedef ... type; // type of stored objects 
        };
                     
        template< typename EC >                                     
        struct iterator_of 
        {
            typedef ... type; // iterator over stored objects
        };
        
        template< typename EC >                                     
        struct const_iterator_of      
        {
            typedef ... type; // iterator over immutable stored objects 
        };

        template< typename EC >                                     
        struct difference_type_of
        {
            typedef ... type; 
            BOOST_STATIC_ASSERT( boost::is_signed< type >::value );
            //
            // remark: if std::iterator_traits<> works for the type, this assertion must hold
            //
            BOOST_STATIC_ASSERT( boost::is_same< type, std::iterator_traits< typename 
                                                       iterator_of< EC >::type >::difference_type>::value );         
        };

        template< typename EC >                                     
        struct size_type_of
        {
            typedef ... type;
            BOOST_STATIC_ASSERT( boost::is_unsigned< type >::value );
            BOOST_STATIC_ASSERT( sizeof( type ) >= sizeof( difference_type_of< EC >::type ) );                   
        };

        template< typename EC >                                     
        struct result_iterator_of     
        {
            typedef ... type; 
            // iterator_of< EC >::type if EC is non-const, const_iterator_of< EC >::type otherwise         
        };
                     
        //
        // funtions
        //
        
        template< typename EC >
        inline typename iterator_of::type
        begin( EC& c );               
    
        template< typename EC >
        inline typename const_iterator_of< EC >::type
        begin( const EC& c );                       
            
        template< typename EC >
        inline typename iterator_of< EC >::type
        end( EC& c );  
                          
        template< typename EC >
        inline typename const_iterator_of< EC >::type
        end( const EC& c );
        
        template< typename EC >
        inline bool
        empty( const EC& c );         
                   
        template< typename EC >
        inline typename size_type_of< EC >::type
        size( const EC& c );
                     
     } // namespace 'boost' 

Library headers

HeaderIncludes
<boost/collection_traits.hpp>everything
<boost/collection_traits/types.hpp>every type-generator
<boost/collection_traits/functions.hpp>every function
<boost/collection_traits/value_type.hpp>value_type_of
<boost/collection_traits/iterator.hpp>iterator_of
<boost/collection_traits/const_iterator.hpp>const_iterator_of
<boost/collection_traits/difference_type.hpp>difference_type_of
<boost/collection_traits/size_type.hpp>size_type_of
<boost/collection_traits/result_iterator.hpp>result_iterator_of
<boost/collection_traits/begin.hpp>begin
<boost/collection_traits/end.hpp>end
<boost/collection_traits/empty.hpp>empty
<boost/collection_traits/size.hpp>size

Semantics

In the table C is a type that conforms to the ExternalCollectionConcept and c is an object of that type. SC will denote a standard container, T[sz] will denote an array of type T of size sz, P will denote std::pair<>, I means an iterator which default construction denotes the end of the range and sc,t,p,i are objects of these types, respectively. Special cases for char* and wchar_t* are described explicitly.

ExpressionReturn typeComplexity
value_type_of<C>::typeSC::value_type
T
std::iterator_traits<P::first_type>::value_type
std::iterator_traits<I>::value_type
compile time
iterator_of<C>::typeSC::iterator
T*
P::first_type
I
compile time
const_iterator_of<C>::typeSC::const_iterator
const T*
P::first_type
I
compile time
difference_type_of<C>::typeSC::difference_type
std::ptrdiff_t
std::iterator_traits<P::first_type>::difference_type
std::iterator_traits<I>::difference_type
compile time
size_type_of<C>::typeSC::size_type
std::size_t
std::size_t
std::size_t
compile time
result_iterator_of<C>::typeconst_iterator_of<C>::type if C is const
iterator_of<C>::type otherwise
compile time

ExpressionReturn typeEffectsComplexity
begin( c )result_iterator_of<C>::typesc.begin()
t
p.first
i
constant time
end( c )result_iterator_of<C>::typesc.end()
t + std::char_traits<C>::length( t ) if C is char* or wchar_t*
t + sz - 1 if C is char[sz] or wchar_t[sz]
t + sz otherwise
p.second
I()
linear if C is char* or wchar_t*
constant time otherwise
empty( c )Convertible to boolsc.empty()
size( t ) == 0
p.first == p.second
begin( i ) == end( i )
linear if C is char* or wchar_t*
constant time otherwise
size( c )size_type_of<C>::typesc.size()
end( t ) - begin( t )
distance( p.first, p.second )
not available for iterators
linear if C is char* or wchar_t*
or if std::distance() is linear
constant time otherwise

Please note that char*,whar_t*,char[], and wchar_t[] behaves differently from normal arrays only for size() and end(). Note that the null pointer is allowed as an argument in these cases.



Examples

Some examples are given in the accompanying test files:


Portability

Full support for built-in arrays require that the compiler supports class template partial specialization.

Notice that some compilers cannot do function template ordering properly. In that case one cannot rely of result_iterator_of<> and a single function definition; instead one needs to supply a function overloaded for const and non-const arguments if it is required.

Full support for iterators like std::istream_iterator<> depends very much on a conforming standard library.

Most of the tests have been run successfully on these compilers


FAQ

  1. Why is there no difference between iterator_of<C>::type and const_iterator_of<C>::type for std::pair<iterator,iterator> or iterators which default construction denotes the end of the range?
  2. In general it is not possible nor desirable to find a corresponding const_iterator. When it is possible to come up with one, the client might choose to construct a std::pair<const_iterator,const_iterator> object.

  3. Why does the traits not supply more types or more functions?

    The traits class have been kept small because its current interface will serve most purposes. If and when a genuine need arises for more functionality, it can be implemented.

  4. How should I implement generic algorithms for external collections?

    One should always start with a generic algorithm that takes two iterators as input. Then use the collection traits to build handier versions on top of the base algorithm.


History

The library have been under way for a long time. Dietmar Kühl originally intended to submit an array_traits<> class template which had most of the functionality present now, but only for arrays and standard containers. Meanwhile work on container algorithms in various context showed the need for handling pairs of iterators, and string libraries needed special treatment of character arrays. Thorsten Ottosen wrote everything from the ground up including the first work-around for missing partial template specialization. Pavol Droba helped to improve the work-around for handicapped compilers and the special character support. The naming scheme of type-generators was suggested by Peter Dimov.


© Thorsten Ottosen 2003-2004 (nesotto_AT_cs.auc.dk). Permission to copy, use, modify, sell and distribute this software is granted provided this copyright notice appears in all copies. This software is provided "as is" without express or implied warranty, and with no claim as to its suitability for any purpose.