CodePlexProject Hosting for Open Source Software

A range aggregator consists of two parts:

- A builder class which does the work
- A helper method to help contruct an instance of the builder class

A builder class has an implicit contract:

// ------------------------------------------------------------------------- // _builder classes: // inherit base_builder // COPYABLE // MOVEABLE (movesemantics) // typedef ... this_type ; // template<typename TRange> // TAggregated build (TRange range) const // -------------------------------------------------------------------------

This means that in order to be a builder class the class has to be:

- Has to inherit
**base_builder** - Copyable
- Moveable
- Has member type called
**this_type**which type is the builder class - Has a member method called
**build**that accepts a range and returns the aggregated value

Let's look at the

struct sum_builder : base_builder { typedef sum_builder this_type ; CPPLINQ_INLINEMETHOD sum_builder () CPPLINQ_NOEXCEPT { } CPPLINQ_INLINEMETHOD sum_builder (sum_builder const & v) CPPLINQ_NOEXCEPT { } CPPLINQ_INLINEMETHOD sum_builder (sum_builder && v) CPPLINQ_NOEXCEPT { } template<typename TRange> CPPLINQ_INLINEMETHOD typename TRange::value_type build (TRange range) { auto sum = typename TRange::value_type (); while (range.next ()) { sum += range.front (); } return std::move (sum); } };

- Inherits
**base_builder** - Copyable
- Moveable
- Has member type
**this_type**that evaluates to**sum_builder** - It has a
**build**method that accepts a range and returns**TRange::value_type**

The

Finally the helper method

```
CPPLINQ_INLINEMETHOD detail::sum_builder sum () CPPLINQ_NOEXCEPT
{
return detail::sum_builder ();
}
```

This is what is needed to make a range aggregator that integrates with CppLinq.

int computes_a_sum () { using namespace cpplinq; int ints[] = {3,1,4,1,5,9,2,6,5,4}; // Computes the sum of all even numbers in the sequence above return from_array (ints) >> where ([](int i) {return i%2 ==0;}) // Keep only even numbers >> sum () // Sum remaining numbers ; }

A range operator consists of three parts:

- A range class which does the work
- A builder class which combined with a range produces a new range
- A helper method to help contruct an instance of the builder class

A range class has an implicit contract:

// ------------------------------------------------------------------------- // _range classes: // inherit base_range // COPYABLE // MOVEABLE (movesemantics) // typedef ... this_type ; // typedef ... value_type ; // typedef ... return_type ; // value_type | value_type const & // enum { returns_reference = 0|1 }; // return_type front () const // bool next () // template<typename TRangeBuilder> // typename get_builtup_type<TRangeBuilder, this_type>::type operator>>(TRangeBuilder range_builder) const // -------------------------------------------------------------------------

This means that in order to be a range class the class has to be:

- Inherits
**base_range** - Copyable
- Moveable
- Has member type called this_type which type is the range class
- Has member type called value_type which is the type of value in the range
- Has member type called
**return_type**which is**value_type const &**or**value_type** - Has an enum member
**returns_reference**which is either**0**or**1**depending if**return_type**is reference or not - Has a member method called front which returns the current value as a
**return_type**- If called on a range where next has not be been called or the previous next returned false the behavior is undefined
**front ()**previously was required to return a value type, this has been relaxed to allow references as well (if its correct to do so)

- Has a member method called next which moves the range to the next value if available (returns
**true**) or returns**false**if no value is available- If called on a range where the previous next returned false next should do nothing and return false
- A range where next never has been should be seen as "pointing" to no-value (as opposed to
**begin ()**which "points" to the first value in a collection)

- Has a member method called operator>> that accepts a range builder and returns the new range
- This is the operator that chains the range expressions together

Let's look at the

template<typename TRange, typename TPredicate> struct where_range : base_range { typedef where_range<TRange, TPredicate> this_type ; typedef TRange range_type ; typedef TPredicate predicate_type ; typedef typename TRange::value_type value_type ; typedef typename TRange::return_type return_type ; enum { returns_reference = TRange::returns_reference , }; range_type range ; predicate_type predicate ; CPPLINQ_INLINEMETHOD where_range ( range_type range , predicate_type predicate ) CPPLINQ_NOEXCEPT : range (std::move (range)) , predicate (std::move (predicate)) { } CPPLINQ_INLINEMETHOD where_range (where_range const & v) : range (v.range) , predicate (v.predicate) { } CPPLINQ_INLINEMETHOD where_range (where_range && v) CPPLINQ_NOEXCEPT : range (std::move (v.range)) , predicate (std::move (v.predicate)) { } template<typename TRangeBuilder> CPPLINQ_INLINEMETHOD typename get_builtup_type<TRangeBuilder, this_type>::type operator>>(TRangeBuilder range_builder) CPPLINQ_NOEXCEPT { return range_builder.build (*this); } CPPLINQ_INLINEMETHOD return_type front () const { return range.front (); } CPPLINQ_INLINEMETHOD bool next () { while (range.next ()) { if (predicate (range.front ())) { return true; } } return false; } };

- Inherits base_range
- Copyable
- Moveable
- Has member type this_type that evaluates to
**where_range** - Has member type called
**value_type**that evalutes to**TRange::value_type** - Has member type called
**return_type**that evalutes to**TRange::return_type** - Has an enum member called
**returns_reference**that evaluates to**TRange::returns_reference****where_range**is easy in that it doesn't transmute the type,**select_range**has to rely on some tricks to figure out the**value_type**

- Has a member method called front which returns the current value as
**return_type** - Has a member method called next which moves the range to the next value if available (returns
**true**) or returns**false**if no value is available - Has a member method called
**operator>>**that accepts a range builder and returns the new range- Uses the meta-function
**get_builtup_type**to figure out the range type

- Uses the meta-function

In addition the builder class has to be provided

template<typename TPredicate> struct where_builder { typedef where_builder<TPredicate> this_type ; typedef TPredicate predicate_type ; predicate_type const predicate ; CPPLINQ_INLINEMETHOD explicit where_builder (predicate_type predicate) CPPLINQ_NOEXCEPT : predicate (std::move (predicate)) { } CPPLINQ_INLINEMETHOD where_builder (where_builder const & v) : predicate (v.predicate) { } CPPLINQ_INLINEMETHOD where_builder (where_builder && v) CPPLINQ_NOEXCEPT : predicate (std::move (v.predicate)) { } template<typename TRange> CPPLINQ_INLINEMETHOD where_range<TRange, TPredicate> build (TRange range) const CPPLINQ_NOEXCEPT { return where_range<TRange, TPredicate>(range, predicate); } };

Finally the helper method

template<typename TPredicate> CPPLINQ_INLINEMETHOD detail::where_builder<TPredicate> where ( TPredicate predicate ) CPPLINQ_NOEXCEPT { return detail::where_builder<TPredicate> (predicate); }

This is what is needed to make a range operator that integrates with CppLinq.

A range source consists of two parts:

- A range class which does the work
- A helper method to help contruct an instance of the range class

The range class has the same implicit contract as above.

Let's look at the

template<typename TValueIterator> struct from_range : base_range { static TValueIterator get_iterator (); typedef from_range<TValueIterator> this_type ; typedef TValueIterator iterator_type ; typedef decltype (*get_iterator ()) raw_value_type ; typedef typename cleanup_type<raw_value_type>::type value_type ; typedef value_type const & return_type ; enum { returns_reference = 1, }; iterator_type current ; iterator_type upcoming; iterator_type end ; CPPLINQ_INLINEMETHOD from_range ( iterator_type begin , iterator_type end ) CPPLINQ_NOEXCEPT : current (std::move (begin)) , upcoming(current) , end (std::move (end)) { } CPPLINQ_INLINEMETHOD from_range (from_range const & v) CPPLINQ_NOEXCEPT : current (v.current) , upcoming(v.upcoming) , end (v.end) { } CPPLINQ_INLINEMETHOD from_range (from_range && v) CPPLINQ_NOEXCEPT : current (std::move (v.current)) , upcoming(std::move (v.upcoming)) , end (std::move (v.end)) { } template<typename TRangeBuilder> CPPLINQ_INLINEMETHOD typename get_builtup_type<TRangeBuilder, this_type>::type operator>>(TRangeBuilder range_builder) const { return range_builder.build (*this); } CPPLINQ_INLINEMETHOD return_type front () const { assert (current != upcoming); assert (current != end); return *current; } CPPLINQ_INLINEMETHOD bool next () CPPLINQ_NOEXCEPT { if (upcoming == end) { return false; } current = upcoming; ++upcoming; return true; } };

Finally the helper method

template<typename TContainer> CPPLINQ_INLINEMETHOD detail::from_range<typename TContainer::const_iterator> from ( TContainer const & container ) { return detail::from_range<typename TContainer::const_iterator> ( container.begin () , container.end () ); }

This is what is needed to make a range source that integrates with CppLinq.

Last edited Mar 23, 2014 at 11:54 AM by marten_range, version 3