Sideshow Bob
Sideshow Bob

Reputation: 4716

How to write a c++ function what can return either iterator or reverse_iterator

As far as I can tell in c++ there is no common base class that covers both iterator and reverse_iterator.

The only suggestion I have seen so far is to get around this using templates ( How to write a function that takes an iterator or collection in a generic way? )

However this solution doesn't seem to work for me.

class MyClass
{
    template<typename Iter> Iter* generate_iterator(...params...)
    {
        //returns either a vector::iterator or vector::reverse_iterator
    }
    template<typename Iter> void do_stuff(Iter *begin, Iter *end)
    {
        //does stuff between elements specified by begin and end
        //I would like this function to remain agnostic of which direction it is working in!
    }
    void caller()
    {
        //I would like this function to remain agnostic of which direction it is working in too...
           do_stuff(generate_iterator(blah),generate_iterator(foo));
    }
};

In this case, generate_iterator() cannot be used as desired because the compiler complains "generate_iterator is not a member of class MyClass" presumably because I haven't specified it (which I can't in practice as caller should be agnostic of the iterator type).

Can anyone help? Thanks in advance!

edit: as Mark B pointed out generate_iterator must return a pointer - now corrected

update: just started using this http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/start_page.html and it seems to work...

Upvotes: 5

Views: 1939

Answers (4)

pmr
pmr

Reputation: 59811

Use boost::variant or boost::any.

boost::variant< reverse_iterator, iterator >
generate_iterator(...) {
  if(...) return iterator();
  else return reverse_iterator();
}

// user code
boost::variant< reverse_iterator, iterator > v = generate_iterator();

if(reverse_iterator* it = boost::get<reverse_iterator>(v))
  ...;
else if(...)
  ...;

Although the variant is better accessed through a visitor.

The downside is that you need some boiler plate to extract the proper type and is exactly the reason why something like any_iterator might be a more sensible choice.

Upvotes: 0

RoundPi
RoundPi

Reputation: 5947

By using boost tuple and boost any , your problem can be easily solved. I wrote a example by using boost::any , see below:

#include <boost/any.hpp>
using boost::any_cast;
#define MSG(msg) cout << msg << endl;
boost::any getIterator(std::vector<int>& vec, bool bReverse)
{
    if(!bReverse)
        return boost::any(vec.begin());
    else
        return boost::any(vec.rbegin());
}

int main() 
{
    std::vector<int> myvec;
    myvec.push_back(1);
    myvec.push_back(2);
    myvec.push_back(3);


    typedef std::vector<int>::iterator vecIter;
    typedef std::vector<int>::reverse_iterator vecRIter;
    try
    {
        boost::any iter  = getIterator(myvec, false);
        boost::any iter2 = getIterator(myvec, true);
        vecIter  it1 = any_cast<vecIter>(iter);
        vecRIter it2 = any_cast<vecRIter>(iter2);
        MSG(*it1);//output 1
        MSG(*it2);//output 3
        return true;
    }
    catch(const boost::bad_any_cast &)
    {
        return false;
    }
}

Upvotes: 1

Mark Ransom
Mark Ransom

Reputation: 308206

You can create your own iterator class that knows how to go both directions. Encapsulate both types of iterator and internally select whichever one you were initialized with.

Here's a start:

template<typename Container>
class BiIterator
{
public:
    BiIterator(Container::iterator i) : m_fwd(i), m_isforward(true) {}
    BiIterator(Container::reverse_iterator i) : m_rev(i), m_isforward(false) {}
    bool operator==(const BiIterator & left, const BiIterator & right);
    Container::value_type & operator*()
    {
        if (m_isforward)
            return *m_fwd;
        return *m_rev;
    }
    const Container::value_type & operator*() const;
    BiIterator & operator++()
    {
        if (m_isforward)
            ++m_fwd;
        else
            ++m_rev;
        return *this;
    }
private:
    Container::iterator         m_fwd;
    Container::reverse_iterator m_rev;
    bool                        m_isforward;
};

Upvotes: 3

Mark B
Mark B

Reputation: 96241

In C++ you can't write a function that returns two different types. In your template case it will return one or the other depending on the instantiation. You could possibly return a base pointer to a polymorphic iterator but that would cause me to ask what you're really trying to do here. Even the standard containers don't try to do that: They have begin and rbegin to distinguish properly. I would suggest having two separate functions that each do the right thing and return one type of iterator or the other as context dictates.

As a side, note that you can't implicitly determine a template instantiation of a type that's only used for the return type of a function.

Upvotes: 2

Related Questions