[/ Copyright 2010 Neil Groves Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) /] [section:extending Extending the library] [section:method_1 Method 1: provide member functions and nested types] This procedure assumes that you have control over the types that should be made conformant to a Range concept. If not, see [link range.reference.extending.method_2 method 2]. The primary templates in this library are implemented such that standard containers will work automatically and so will __boost_array__. Below is given an overview of which member functions and member types a class must specify to be useable as a certain Range concept. [table [[Member function] [Related concept ]] [[`begin()` ] [__single_pass_range__]] [[`end()` ] [__single_pass_range__]] ] Notice that `rbegin()` and `rend()` member functions are not needed even though the container can support bidirectional iteration. The required member types are: [table [[Member type ] [Related concept ]] [[`iterator` ] [__single_pass_range__]] [[`const_iterator`] [__single_pass_range__]] ] Again one should notice that member types `reverse_iterator` and `const_reverse_iterator` are not needed. [endsect] [section:method_2 Method 2: provide free-standing functions and specialize metafunctions] This procedure assumes that you cannot (or do not wish to) change the types that should be made conformant to a Range concept. If this is not true, see [link range.reference.extending.method_1 method 1]. The primary templates in this library are implemented such that certain functions are found via argument-dependent-lookup (ADL). Below is given an overview of which free-standing functions a class must specify to be useable as a certain Range concept. Let `x` be a variable (`const` or `mutable`) of the class in question. [table [[Function ] [Related concept ]] [[`range_begin(x)`] [__single_pass_range__]] [[`range_end(x)` ] [__single_pass_range__]] [[`range_calculate_size(x)`] [ Optional. This can be used to specify a mechanism for constant-time computation of the size of a range. The default behaviour is to return `boost::end(x) - boost::begin(x)` for random access ranges, and to return `x.size()` for ranges with lesser traversal capability. This behaviour can be changed by implementing `range_calculate_size` in a manner that will be found via ADL. The ability to calculate size in O(1) is often possible even with ranges with traversal categories less than random access.]] ] `range_begin()` and `range_end()` must be overloaded for both `const` and `mutable` reference arguments. You must also specialize two metafunctions for your type `X`: [table [[Metafunction ] [Related concept ]] [[`boost::range_mutable_iterator`] [__single_pass_range__]] [[`boost::range_const_iterator`] [__single_pass_range__]] ] A complete example is given here: `` #include #include // for std::iterator_traits, std::distance() namespace Foo { // // Our sample UDT. A 'Pair' // will work as a range when the stored // elements are iterators. // template< class T > struct Pair { T first, last; }; } // namespace 'Foo' namespace boost { // // Specialize metafunctions. We must include the range.hpp header. // We must open the 'boost' namespace. // template< class T > struct range_mutable_iterator< Foo::Pair > { typedef T type; }; template< class T > struct range_const_iterator< Foo::Pair > { // // Remark: this is defined similar to 'range_iterator' // because the 'Pair' type does not distinguish // between an iterator and a const_iterator. // typedef T type; }; } // namespace 'boost' namespace Foo { // // The required functions. These should be defined in // the same namespace as 'Pair', in this case // in namespace 'Foo'. // template< class T > inline T range_begin( Pair& x ) { return x.first; } template< class T > inline T range_begin( const Pair& x ) { return x.first; } template< class T > inline T range_end( Pair& x ) { return x.last; } template< class T > inline T range_end( const Pair& x ) { return x.last; } } // namespace 'Foo' #include int main(int argc, const char* argv[]) { typedef std::vector::iterator iter; std::vector vec; Foo::Pair pair = { vec.begin(), vec.end() }; const Foo::Pair& cpair = pair; // // Notice that we call 'begin' etc with qualification. // iter i = boost::begin( pair ); iter e = boost::end( pair ); i = boost::begin( cpair ); e = boost::end( cpair ); boost::range_difference< Foo::Pair >::type s = boost::size( pair ); s = boost::size( cpair ); boost::range_reverse_iterator< const Foo::Pair >::type ri = boost::rbegin( cpair ), re = boost::rend( cpair ); return 0; } `` [endsect] [section:method_3 Method 3: provide range adaptor implementations] [section:method_3_1 Method 3.1: Implement a Range Adaptor without arguments] To implement a Range Adaptor without arguments (e.g. reversed) you need to: # Provide a range for your return type, for example: `` #include #include template< typename R > struct reverse_range : boost::iterator_range< boost::reverse_iterator< typename boost::range_iterator::type> > { private: typedef boost::iterator_range< boost::reverse_iterator< typename boost::range_iterator::type> > base; public: typedef boost::reverse_iterator< typename boost::range_iterator::type > iterator; reverse_range(R& r) : base(iterator(boost::end(r)), iterator(boost::begin(r))) { } }; `` # Provide a tag to uniquely identify your adaptor in the `operator|` function overload set `` namespace detail { struct reverse_forwarder {}; } `` # Implement `operator|` `` template< class BidirectionalRng > inline reverse_range operator|( BidirectionalRng& r, detail::reverse_forwarder ) { return reverse_range( r ); } template< class BidirectionalRng > inline reverse_range operator|( const BidirectionalRng& r, detail::reverse_forwarder ) { return reverse_range( r ); } `` # Declare the adaptor itself (it is a variable of the tag type). `` namespace { const detail::reverse_forwarder reversed = detail::reverse_forwarder(); } `` [endsect] [section:method_3_2 Method 3.2: Implement a Range Adaptor with arguments] # Provide a range for your return type, for example: `` #include #include #include template class replace_value { public: typedef const Value& result_type; typedef const Value& argument_type; replace_value(const Value& from, const Value& to) : m_from(from), m_to(to) { } const Value& operator()(const Value& x) const { return (x == m_from) ? m_to : x; } private: Value m_from; Value m_to; }; template class replace_range : public boost::iterator_range< boost::transform_iterator< replace_value::type>, typename boost::range_iterator::type> > { private: typedef typename boost::range_value::type value_type; typedef typename boost::range_iterator::type iterator_base; typedef replace_value Fn; typedef boost::transform_iterator replaced_iterator; typedef boost::iterator_range base_t; public: replace_range(Range& rng, value_type from, value_type to) : base_t(replaced_iterator(boost::begin(rng), Fn(from,to)), replaced_iterator(boost::end(rng), Fn(from,to))) { } }; `` # Implement a holder class to hold the arguments required to construct the RangeAdaptor. The holder combines multiple parameters into one that can be passed as the right operand of `operator|()`. `` template class replace_holder : public boost::range_detail::holder2 { public: replace_holder(const T& from, const T& to) : boost::range_detail::holder2(from, to) { } private: void operator=(const replace_holder&); }; `` # Define an instance of the holder with the name of the adaptor `` static boost::range_detail::forwarder2 replaced = boost::range_detail::forwarder2(); `` # Define `operator|` `` template inline replace_range operator|(SinglePassRange& rng, const replace_holder::type>& f) { return replace_range(rng, f.val1, f.val2); } template inline replace_range operator|(const SinglePassRange& rng, const replace_holder::type>& f) { return replace_range(rng, f.val1, f.val2); } `` [endsect] [endsect] [endsect]