nos
nos

Reputation: 229058

"Generic" iterator in c++

I have:

void add_all_msgs(std::deque<Message>::iterator &iter);

How can I make that function "generic", so it can take any kind of inputiterators ? I don't really care if it's iterating a deque,a vector or something else, as long as the iterator is iterating Message's. - is this at all straight forward possible in c++ ?

Upvotes: 13

Views: 6631

Answers (7)

Marc Mutz - mmutz
Marc Mutz - mmutz

Reputation: 25283

Slightly simpler that the above (in that it leverages existing libraries):

#include <boost/static_assert.hpp> // or use C++0x static_assert
#include <boost/type_traits/is_same.hpp>

template <typename InputIterator>
void add_all_msgs( InputIterator it ) {
    BOOST_STATIC_ASSERT(( boost::is_same<
        typename std::iterator_traits<InputIterator>::value_type,
        Message>::value ));
    // ...

Upvotes: 1

Steve Jessop
Steve Jessop

Reputation: 279215

It's difficult to have dynamic polymorphism with C++-style iterators. operator++(int) returns by value, which afaik is intractable: you can't have a virtual member function which returns *this by value without it being sliced.

If possible, I recommend using templates as everyone else says.

However if you do need dynamic polymorphism, for example because you can't expose the implementation of add_all_msgs as a template would do, then I think you could pretend to be Java, like this:

struct MessageIterator {
    virtual Message &get() = 0;
    virtual void next() = 0;
    // add more functions if you need more than a Forward Iterator.
    virtual ~MessageIterator() { };  // Not currently needed, but best be safe
};

// implementation elsewhere. Uses get() and next() instead of * and ++
void add_all_msgs(MessageIterator &it);

template <typename T>
struct Adaptor : public MessageIterator {
    typename T::iterator wrapped;
    Adaptor(typename T::iterator w) : wrapped(w) { }
    virtual Message &get() {
        return *wrapped;
    }
    virtual void next() {
        ++wrapped;
    }
};

int main() {
    std::deque<Message> v;
    Adaptor<std::deque<Message> > a(v.begin());
    add_all_msgs(a);
}

I've checked that this compiles, but I haven't tested it and I've never used this design before. I also haven't bothered with const-ness - in practice you probably want a const Message &get() const. And at the moment the adaptor has no way of knowing when to stop, but then neither does the code you started with, so I've ignored that too. Basically you'd need a hasNext function which compares wrapped against an end iterator supplied to the constructor.

You might be able to do something with a template function and const references, so that the client doesn't have to know about or declare that nasty Adaptor type.

[Edit: come to think of it, it's probably better to have a stub add_all_msgs function template, that wraps its parameter in an Adaptor and then calls real_add_all_msgs. This completely hides the adaptor from the client.]

Upvotes: 1

Greg Rogers
Greg Rogers

Reputation: 36429

If you don't want to templatize your add_all_msgs function, you can use adobe::any_iterator:

typedef adobe::any_iterator<Message, std::input_iterator_tag> any_message_iterator;
void add_all_msgs(any_message_iterator begin, any_message_iterator end);

Upvotes: 2

aJ.
aJ.

Reputation: 35450

template<class InputIterator>
void add_all_msgs(InputIterator iter);

Usage:

std::deque<Message> deq;
add_all_msgs(deq.begin());

Upvotes: 8

Paolo Capriotti
Paolo Capriotti

Reputation: 4072

If you want the compiler to check whether the iterator actually refers to Message objects, you can use a technique like the following.

template <typename InputIterator, typename ValueType>
struct AddAllMessages { };

template <typename InputIterator>
struct AddAllMessages<InputIterator, Message> {
  static void execute(const InputIterator &it) {
    // ...
  }
};

template <typename InputIterator>
void add_all_msgs(const InputIterator &it) {
  AddAllMessages<InputIterator, 
                 typename std::iterator_traits<InputIterator>::value_type>::execute(it);
}

Upvotes: 5

Edouard A.
Edouard A.

Reputation: 6128

template <typename Iterator>
void add_all_messages(Iterator first, Iterator last)

usage :

vector<message> v;
add_all_messages(v.begin(), v.end());

You need to specify the end, otherwise you won't know when to stop! It also gives you the flexibility of adding only a subrange of a container.

Upvotes: 13

Indy9000
Indy9000

Reputation: 8851

#include <deque>
#include <vector>
#include <list>
#include <string>
using namespace std;

template<typename T>
void add_all_msgs(T &iter)
{

}

int _tmain(int argc, _TCHAR* argv[])
{
    std::deque<string>::iterator it1;
    std::vector<string>::iterator it2;
    std::list<string>::iterator it3;

    add_all_msgs(it1);
    add_all_msgs(it2);
    add_all_msgs(it3);


    return 0;
}

Upvotes: 0

Related Questions