c++boost.gif (8819 bytes)array_traits.hpp

Abstract

Although STL somewhat supports built-in arrays, the standard library provides no support for getting iterators to built-in arrays. While getting an iterator to the beginning of an array is trivial, getting a past-the-end iterator is much harder, if not impossible (at least portably). However, it is possible to get a past-the-end iterator for statically sized built-in arrays. This component provides functions for better support of built-in arrays.

Synopsis

#include "boost/array_traits.hpp"

namespace boost
{
  template <typename Container>
    struct array_traits
    {
      typedef ... iterator;
      typedef ... size_type;

      static iterator  begin();
      static iterator  end();
      static size_type size();
    };

  template <typename Container>
    array_traits<Container>::iterator  begin(Container &c);
  template <typename Container>
    array_traits<Container>::iterator  end(Container &c);
  template <typename Container>
    array_traits<Container>::size_type size(Container &c);

  template <typename T, unsigned long sz>
  char (&sizer(T (&)[sz]))[sz];
}
  

Description

The header boost/array_traits.hpp declares one template class and a set of functions which can be used for better support of statically sized built-in arrays. See the article about built-in arrays for a rationale why and under which constraints built-in arrays are still useful (for most applications, container classes, whether from the standard library, some third party, or self-written, are superior than built-in arrays but there are a few exceptions to this rule).

The template class array_traits is mainly an auxiliary class used by the functions to get a consistent interface. It is specialized for statically sized arrays and constant containers. The former does not explicitly define an iterator type and the methods to access the beginning, the end, and the size of this container is very different from the approach taken for STL containers. It is necessary to specialize for constant containers because the iterator type is a different one than for non-constant containers (const_iterator instead of iterator).

The functions begin(), end(), and size() can be used to replace the corresponding container member functions: For STL conformant containers, these functions simply call the corresponding container member functions. However, for statically sized built-in arrays, they behave different. In this case, begin() simply returns a pointer to the array (the type passed to the function begin() is T (&)[size], not T*), end() converts the argument to a pointer and adds the size of the array, and size() returns the size.

Here is a sample program using these functions:

 
#include "boost/array_traits.hpp"
#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
  int              arr[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  std::vector<int> vec(boost::begin(arr), boost::end(arr));

  std::ostream_iterator<int> oit(std::cout, " ");

  std::cout << "size:  " << boost::size(arr) << "\n";
  std::copy(boost::begin(arr), boost::end(arr), oit);
  std::cout << "\n";

  std::cout << "size:  " << boost::size(vec) << "\n";
  std::copy(boost::begin(vec), boost::end(vec), oit);
  std::cout << "\n";

  return 0;
}
    
Note that this example uses absolutely identical code to print the array and the vector. Using the same code is essential when writing templates. However, using end(array) instead of either hardcoding the size of the array or using some macro is also convenient in other contexts when a statically size array is used (using a macro for this is as convenient as using the function end() but it is not safe: the macro could be called on a pointer, too, yielding false results).

Actually, this example should use a template function which gets the container as argument. This is not used because none of the compilers used to test this code could compile it...

Bugs

Currently, this code needs (b)leading edge compilers. Thus, only few compilers compile this code successfully. I was able to get the code working for egcs-2.92.23 (that is, the snapshot from 1998-11-22) and EDG-2.39 but I needed some extra code for both compilers to make it work. Although this code should not be harmful to standard conforming compilers, it should be remove once it is no longer necessary. The only other compiler I tested was MSVC++ 6.0 but this failed with no hope to make things work... (this compiler already flagged errors when only the array version of the three functions were used; I gave up before coming around to test whether partial specialization needed for array_traits works).

The result of the size() function cannot be used as an integral constant. This is the major flaw with this code. It is possible to write a macro which is an integral constant... However, in a discussion in comp.lang.c++.moderated a safe method for getting the size of an array as integral constant was proposed. The basic idea is to use the sizeof operator to a function returning a reference to an appropriately sized array of chars: The result of the sizeof operator applied to this function can be used as an integral constant. To do this, the function sizer() is declared (it is not necessary to define this function since the argument to the sizeof operator is never evaluated) is declared in boost/array_traits.hpp. Here is an example:

 
#include "boost/array_traits.hpp"
#include <algorithm>

int main()
{
  int source[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  int destination[sizeof(boost::sizer(source))];

  std::copy(boost::begin(source), boost::end(source), boost::begin(destination));
}
    

See Also

array-article(3)

History

The header was previously named array.hpp


Copyright © 1998 Dietmar Kühl (dietmar.kuehl@claas-solutions.de)
Claas Solutions GmbH