Reputation: 5269
I'd like to have a function as described in title.
I've noticed that STL algorithms that work with containers of any type (list, vector, etc) containing elements of any type (int, double) provide genericity by using iterator types as template parameters, e.g.
template<typename _II, typename _OI>
inline _OI
copy(_II __first, _II __last, _OI __result)
This is a good method until the algorithm works for any type of elements. The only requirement for element type is that it must have copy constructor.
But suppose we have one concrete type
class MyElement
{
public:
void doSomethingWithElement();
};
and we want to implement a function that processes number of elements of this type by calling function doSomethingWithElement()
.
Writing a function that receives container of specific type is not very convenient because many containers are treated in the same way (e.g. iterators), and if there will be need for processing containers of different types we'll be forced to duplicate the code. Writing a template works fine, but it seems ugly because we have to implement function in place where it is declared (in header file). Also, when we want to process elements of only one type, parametrizing this type is not the right way to achieve the goal.
I've been thinking about iterator interface that could be used like
void processContainer(IIterator<MyElement> begin, IIterator<MyElement> end);
If this iterator had pure virtual operator++ and operator* that were implemented in derived classes, we could pass such objects to processContainer
. But there is a problem: if IIterator is abstract class, we can't instantiate it in the implementation of processContainer
, and if we pass a pointer to IIterator, this function will be able to modify it.
Does anybody know any other hack to do this? Or would be another approach better than these ones above? Thanks in advance.
Upvotes: 4
Views: 810
Reputation: 2999
It's not possible to create an abstract iterator that has the full iterator functionality - including the ability to make copies of itself - without changing the iterator interface. However, you can implement a subset of iterator functionality using an abstract base class:
#include <iterator>
#include <vector>
#include <list>
template<typename T>
struct AbstractIterator
{
virtual bool operator!=(const AbstractIterator<T>& other) const = 0;
virtual void operator++() = 0;
virtual T& operator*() = 0;
};
template<typename Iterator>
struct ConcreteIterator : AbstractIterator<typename std::iterator_traits<Iterator>::value_type>
{
typedef typename std::iterator_traits<Iterator>::value_type value_type;
Iterator i;
ConcreteIterator(Iterator i) : i(i)
{
}
virtual bool operator!=(const AbstractIterator<value_type>& other) const
{
return i != static_cast<const ConcreteIterator*>(&other)->i;
}
virtual void operator++()
{
++i;
}
virtual value_type& operator*()
{
return *i;
}
};
template<typename Iterator>
ConcreteIterator<Iterator> wrapIterator(Iterator i)
{
return ConcreteIterator<Iterator>(i);
}
class MyElement
{
public:
void doSomethingWithElement();
};
void processContainerImpl(AbstractIterator<MyElement>& first, AbstractIterator<MyElement>& last)
{
for(; first != last; ++first)
{
(*first).doSomethingWithElement();
}
}
template<typename Iterator>
void processContainer(Iterator first, Iterator last)
{
ConcreteIterator<Iterator> wrapFirst = wrapIterator(first);
ConcreteIterator<Iterator> wrapLast = wrapIterator(last);
return processContainerImpl(wrapFirst, wrapLast);
}
int main()
{
std::vector<MyElement> v;
processContainer(v.begin(), v.end());
std::list<MyElement> l;
processContainer(l.begin(), l.end());
}
Upvotes: 0
Reputation: 40859
You can't do exactly what you want to do. You could use enable_if
to limit the function's availability:
template < typename Container >
typename enable_if<is_same<typename Container::value_type, MyElement>,void>::type processContainer(Container c)...
Upvotes: 0
Reputation: 2273
You could use std::for_each(): http://www.cplusplus.com/reference/algorithm/for_each/
full code:
void callDoSomething(MyElement &elem)
{
elem.doSomething();
}
int main()
{
std::vector<MyElement> vec(100);
std::for_each(vec.begin(), vec.end(), callDoSomething);
}
Upvotes: 0
Reputation: 208323
The simpler approach is to ignore the restriction and just implement your function as a template for any iterator. If the iterator does not refer to the type, then the user will get a horrible error message in the lines of "type X does not have doSomethingWithElement
member function`.
The next thing would be to provide a static_assert
, the function would still take any iterator (meaning that it will participate in overload resolution for any type) but the error message will be slightly more informative.
Furthermore you can decide to use SFINAE to remove the overload from the set of candidates. But while SFINAE is a beautiful golden hammer, I am not sure that you have the appropriate nail at hand.
If you really want to go further down the lane, you can take a look at any_iterator
in the Adobe libraries as an example on how to perform type erasure on the iterator type to avoid the template. The complexity of this approach is orders of magnitude higher than any of the previous, and the runtime cost will also be higher, providing the only advantage of a cleaner ABI and less code size (different iterators can be passed to a single function).
Upvotes: 5