tags:

views:

226

answers:

2

I want to specify in a header file that the input to a function will be an iterator_range, and the iterator can be dereferenced to obtain an int. What is the best way to do this? I do not want to say iterator_range<std::vector<int>::iterator> as this binds the implementation to use std::vector<int>.

Would appreciate any ideas to specify the function the best way.

+3  A: 

A common way to do this is to make the range a template parameter and then let the usage you make of it be the "concept check":

template<class SinglePassRange>
void func(SinglePassRange const & nums)
{
   typedef typename boost::range_iterator<SinglePassRange>::type It;
   for(It it = nums.begin(), e = nums.end(); it != e; ++it)
   {
       int i = *it; 
       // Do something with your int
   } 
}

This won't compile if your range does not contain ints (or something convertible to int) so there's no need to add any further constraints to the interface. But if you really want you can add a concept check at the begining of your function (it will provide better error messages to your clients):

BOOST_CONCEPT_ASSERT(
    (boost::Convertible<typename boost::range_value<SinglePassRange>::type,
                        int>));

Finally, if you don't want to make your function a template then I think that you'll have to cope with taking a boost::iterator_range<std::vector<int>::iterator> but in that case I see no advantage with respect to taking a simple std::vector&.

Manuel
Doesn't answer my question, but useful information indeed. +1
Amit Kumar
Thanks. Also, are you sure that my last sentence does not answer your question? I mean, a negative answer is also an answer :)
Manuel
@Manuel Yes I agree. Thanks for correction.
Amit Kumar
@Amit - See my edit to the last sentence
Manuel
@Manuel Yes, makes sense. I should probably just typedef the vector and receive it as input. Alternatively receive a typedef of the iterator_range.
Amit Kumar
+1  A: 

Your question seems a bit unclear and/or the requirements are contradictory.

It seems you want a function to take a boost::iterator_range<Container<int>::iterator> where Container can be anything, yet you don't want a function template.

I don't see how that can be achieved, except by overloading the function for all the iterators you want to support (gets even worse if you don't strictly mean a container of int).


Here's something just for fun, a quick any_iterator that uses type-erasure so that it can indeed wrap any iterator (which dereferences to a particular type). (Actually by Google'ing you might be able to find a more complete class with the same name.)

It is used by a non-template sum function:

#include <numeric>
#include <vector>
#include <list>
#include <iostream>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/range/iterator_range.hpp>

namespace detail
{
template <class T>
class any_iterator_base
{
public:
    virtual ~any_iterator_base() {}
    virtual void increment() = 0;
    virtual bool equal(const any_iterator_base& other) const = 0;
    virtual T& dereference() const = 0;
};

template <class T, class Iter>
class any_iterator_impl: public any_iterator_base<T>
{
    Iter it;
public:
    any_iterator_impl(Iter it): it(it) {}
    virtual void increment() { ++it; }
    virtual bool equal(const any_iterator_base& other) const
    {
        //warning: throws if dynamic type of other is not the same as *this (can't compare iterators of different type)
        return it == dynamic_cast<const any_iterator_impl<T, Iter>&>(other).it;
    }
    virtual T& dereference() const { return *it; }
};

} //namespace detail

template <class T>
class any_iterator: public boost::iterator_facade<any_iterator<T>, T, boost::forward_traversal_tag>
{
    boost::shared_ptr<detail::any_iterator_base<T> > iter;
public:
    template <class Iter>
    any_iterator(Iter it): iter(new detail::any_iterator_impl<T, Iter>(it)) {}
private:
    friend class boost::iterator_core_access;

    void increment() { iter->increment(); }

    bool equal(const any_iterator& other) const
    {
        return iter->equal(*other.iter);
    }

    T& dereference() const { return iter->dereference(); }
};

int sum(const boost::iterator_range<any_iterator<int> >& range)
{
    return std::accumulate(range.begin(), range.end(), 0);
}

int main()
{
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    std::vector<int> vec(arr, arr + 5);
    std::list<int> li(arr + 5, arr + 10);
    std::cout << sum(boost::make_iterator_range(any_iterator<int>(vec.begin()), any_iterator<int>(vec.end()))) << '\n';
    std::cout << sum(boost::make_iterator_range(any_iterator<int>(li.begin()), any_iterator<int>(li.end()))) << '\n';
    std::cout << sum(boost::make_iterator_range(any_iterator<int>(arr), any_iterator<int>(arr + 10))) << '\n';
}

(I'd still doubt, whether it is a practical solution. Templates are made for these things.)

UncleBens
I understand now what you mean. You're right that it can't be done without function templates. +1
Amit Kumar