I have:
void add_all_msgs(std::deque::iterator &iter);
How can I make that function \"generic\", so it can take any kind
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 ));
// ...
#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;
}
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.
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.]
template<class InputIterator>
void add_all_msgs(InputIterator iter);
Usage:
std::deque<Message> deq;
add_all_msgs(deq.begin());
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);