Simply put, if I have a set and vector how do I create a generic method that can handle both as params.
All I want to do, is iterate over either types of collections. Sounds like it should be trivial but I'm missing something.
void printMeSomeStrings(somebaseclass<string> strings) {
for (auto& str : strings) {
cout << str << endl;
}
}
In C#, I would pass IEnumerable or something like that. Then I could iterate over the collection.
Any general reading explaining the answer would be appreciated.
The first option is to put the code doing the iterating in a template. This requires exposing the implementation to everyone who uses it, which has disadvantages.
Basically, take a type C
as a template
parameter, then write your code in terms of that type C
.
template<typename C>
void printMeSomeStrings(C&& strings) {
for (auto const& str : strings) {
cout << str << endl;
}
}
If you want to be able to have a strong barrier between interface and implementation, the C++11 approach would be to engage in type erasure on a for
-iterable container, and then expose a for
-iterable container, like how std::function
works.
This is trickier. I personally find writing a for_each
function easier than writing a full blown iteration adapter. If you want the full blown container iteration type erasure object, start with boost
, or ask me below and I might do it.
The for_each
adaptor is easy, however.
#include <functional>
#include <utility>
#include <iterator>
#include <memory>
template<typename T>
struct for_each_helper_interface {
virtual ~for_each_helper_interface() {}
virtual void for_each( std::function< void(T) > const& ) = 0;
};
template<typename C, typename T>
struct for_each_helper:for_each_helper_interface<T> {
C& c;
for_each_helper( C& in ):c(in) {}
virtual void for_each( std::function< void(T) > const& f ) override final {
for( auto&& x:c ) {
f(x);
}
}
};
template<typename T>
struct for_each_adaptor {
std::unique_ptr<for_each_helper_interface<T>> pImpl;
void for_each( std::function< void(T) > const& f ) {
if (pImpl) {
pImpl->for_each(f);
}
}
template<typename C>
for_each_adaptor( C&& c ): pImpl( new for_each_helper<C, T>( std::forward<C>(c) ) ) {}
};
which will type-erase the container of T
(or a type convertible to T
!) and expose a for_each
method that lets you iterate over the contents of the container. Use like this:
#include <set>
#include <iostream>
#include <vector>
void print_stufF( for_each_adaptor<std::string const&> c ) {
c.for_each([&](std::string const&s){
std::cout << s << "\n";
});
}
int main() {
std::set<std::string> s;
s.insert("hello");
s.insert("world");
print_stuff(s);
std::vector<std::string> v;
v.push_back("hola");
v.push_back("bola");
print_stuff(v);
}
What is going on here is that for each type used to construct our adaptor, we build a custom implementation of for each. We then store a pointer to the abstract base class of this custom class, and redirect for each calls to it.
This means anything that specializes std::begin
or defines its own begin need not be related: we create ad hoc relationships at point of use instead.
Live example: http://ideone.com/xOqBkI