...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
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 range_begin() and range_end() in the // same namespace as sub_string, to be found by Argument-Dependent Lookup. inline std::string::iterator range_begin( sub_string & x ) { return x.begin; } inline std::string::iterator 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 range_begin( sub_string const & x ) { return x.begin; } inline std::string::const_iterator range_end( sub_string const & x ) { return x.end; } } namespace boost { // specialize range_mutable_iterator and range_const_iterator in namespace boost template<> struct range_mutable_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.
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
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.