| Home | Libraries | People | FAQ | More

## Extensibility

If we want to use `BOOST_FOREACH` to iterate over some new collection type, we must "teach" `BOOST_FOREACH` how to interact with our type. Since `BOOST_FOREACH` is built on top of Boost.Range, we must extend Boost.Range in order to extend `BOOST_FOREACH`. The section Extending Boost.Range explores this topic in detail.

Below is an example for extending `BOOST_FOREACH` to iterate over a sub-string type, which contains two iterators into a `std::string`.

    namespace my
    {
        // sub_string: part of a string, as delimited by a pair
        // of iterators
        struct sub_string
        {
            std::string::iterator begin;
            std::string::iterator end;

            /* ... implementation ... */
        };

        // Add overloads of boost_range_begin() and boost_range_end() in the
        // same namespace as sub_string, to be found by Argument-Dependent Lookup.

        inline std::string::iterator boost_range_begin( sub_string & x )
        {
            return x.begin;
        }

        inline std::string::iterator boost_range_end( sub_string & x )
        {
            return x.end;
        }

        // Also add overloads for const sub_strings. Note we use the conversion
        // from string::iterator to string::const_iterator here.

        inline std::string::const_iterator boost_range_begin( sub_string const & x )
        {
            return x.begin;
        }

        inline std::string::const_iterator boost_range_end( sub_string const & x )
        {
            return x.end;
        }
    }

    namespace boost
    {
        // specialize rannge_iterator and range_const_iterator in namespace boost
        template<>
        struct range_iterator< my::sub_string >
        {
            typedef std::string::iterator type;
        };

        template<>
        struct range_const_iterator< my::sub_string >
        {
            typedef std::string::const_iterator type;
        };
    }

Now that we have taught Boost.Range (and hence `BOOST_FOREACH`) about our type, we can now use `BOOST_FOREACH` to iterate over our sub_string type.

    my::sub_string substr;
    BOOST_FOREACH( char ch, substr )
    {
        // Woo-hoo!
    }

There are some portability issues we should be aware of when extending `BOOST_FOREACH`. Be sure to check out the Portability section. In particular, if your compiler does not support Argument-Dependent Lookup, the Boost.Range Portability section offers some suggested work-arounds.

## Making `BOOST_FOREACH` Work with Non-Copyable Sequence Types

For sequence types that are non-copyable, we will need to tell `BOOST_FOREACH` to not try to make copies. If our type inherits from `boost::noncopyable`, no further action is required. If not, we must specialize the `boost::foreach::is_noncopyable<>` template, as follows:

    class noncopy_vector
    {
        // ...
    private:
        noncopy_vector( noncopy_vector const & ); // non-copyable!
    };

    namespace boost { namespace foreach
    {
        template<>
        struct is_noncopyable< noncopy_vector >
          : mpl::true_
        {
        };
    }}

Another way to achieve the same effect is to override the global `boost_foreach_is_noncopyable()` function. Doing it this way has the advantage of being portable to older compilers.

    // At global scope...
    inline boost::mpl::true_ *
    boost_foreach_is_noncopyable( noncopy_vector *&, boost::foreach::tag )
    {
        return 0;
    }

| Tip

Even though we have to tell `BOOST_FOREACH` that our type is non-copyable, that doesn't mean that `BOOST_FOREACH` always makes a copy of our sequence type. Obviously, doing so would be expensive and even wrong in some cases. `BOOST_FOREACH` is quite smart about when to make a copy and when not to. The `is_noncopyable<>` trait is needed to elide the copy, which is on a branch that might never get taken.

## Optimizing `BOOST_FOREACH` for Lightweight Proxy Sequence Types

On some compilers, `BOOST_FOREACH` must occasionally take a slightly slower code path to guarantee correct handling of sequences stored in temporary objects. It asks itself, "Should I make a copy of this object?" and later, "Did I make a copy or not?" For some types of sequences, this is overkill. Consider a sequence which is a simple pair of iterators. Jumping through hoops of fire to avoid copying it doesn't make sense because copying it is so cheap.

A pair of iterators is an example of a lightweight proxy. It does not store the values of the sequence; rather, it stores iterators to them. This means that iterating over a copy of the proxy object will give the same results as using the object itself. For such types, `BOOST_FOREACH` provides a hook that lets us tell it not to worry about the expense of making a copy. This can result in slightly faster loop execution. Simply specialize the `boost::foreach::is_lightweight_proxy<>` trait, as follows:

    struct sub_string
      : boost::iterator_range< std::string::iterator >
    {
        // ...
    };

    namespace boost { namespace foreach
    {
        template<>
        struct is_lightweight_proxy< sub_string >
          : mpl::true_
        {
        };
    }}

Alternately, we could achieve the same effect by overriding the global `boost_foreach_is_lightweight_proxy()` function, as follows:

    // At global scope...
    inline boost::mpl::true_ *
    boost_foreach_is_lightweight_proxy( sub_string *&, boost::foreach::tag )
    {
        return 0;
    }

This method is portable to older compilers.

| Copyright © 2004 Eric Niebler