tags:

views:

822

answers:

7

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++ ?

+5  A: 
template<class InputIterator>
void add_all_msgs(InputIterator iter);

Usage:

std::deque<Message> deq;
add_all_msgs(deq.begin());
aJ
Convention is to take iterators by value rather than by reference. Aside from that iterators are normally "small" anyway, the reason for this (afaik) is to allow the caller to pass a temporary, such as a return value from std::back_inserter. C++0x helps with this in at least two ways I can think of.
Steve Jessop
Heh, or the return value from "begin" come to think of it. The above "usage" code doesn't compile: "invalid initialization of non-const reference of type 'blah' from a temporary of type 'blah'".
Steve Jessop
The OP explicitly said: "as long as the iterator is iterating Message". This solution, and most of the others, completely ignored this requirement.
Paolo Capriotti
@Pauolo: This would be covered by Concepts in C++0x, but for now, we'll just have to make do with assigning or constructing a Message from *iter and the compiler will tell us if it can't.
Novelocrat
You can do it in C++98 too. See my answer below, or the one using BOOST_STATIC_ASSERT. The error message is not as good as the one you would get using concepts, but at least it's right at the point of the error in the instantiation stack.
Paolo Capriotti
A: 
#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;
}
Indeera
And the add_all_msgs have to be a template more or less ? The iterators arn't "polymorphic" ?
nos
@noselasd: The iterators do not derive from a common base class, if that's what you mean.
Chris Jester-Young
So they _are_ polymorphic (via static duck typing), just not in the way you might think of polymorphism in, say, Java.
Chris Jester-Young
+6  A: 
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.

Edouard A.
Well, I actually assumed (unlike aJ) that the iterator is used as an output iterator, in which case, the ending iterator is superfluous. :-P
Chris Jester-Young
Oh wait, the OP says it's an input iterator. Point taken. :-P
Chris Jester-Young
+2  A: 

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);
}
Paolo Capriotti
A: 

Slightly simpler that the above (in that it leverages existing libraries): #include // or use C++0x static_assert #include

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

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);
Greg Rogers
Nifty. [15chars]
Steve Jessop
I'll go with templating the add_all_msgs for now, though this is what I'd ideally wanted .
nos
The template version is certainly more efficient, with any_iterator you have a virtual function call on every operation (ie moving to the next position, and dereferencing) versus an inline'd function call.
Greg Rogers
+1  A: 

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.]

Steve Jessop