With almost all code I write, I am often dealing with set reduction problems on collections that ultimately end up with naive \"if\" conditions inside of them. Here\'s a simple
Here is a quick relatively minimal filter
function.
It takes a predicate. It returns a function object that takes an iterable.
It returns an iterable that can be used in a for(:)
loop.
template
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
bool empty() const { return begin()==end(); }
};
template
range_t range( It b, It e ) { return {std::move(b), std::move(e)}; }
template
struct filter_helper:range_t {
F f;
void advance() {
while(true) {
(range_t&)*this = range( std::next(this->begin()), this->end() );
if (this->empty())
return;
if (f(*this->begin()))
return;
}
}
filter_helper(range_t r, F fin):
range_t(r), f(std::move(fin))
{
while(true)
{
if (this->empty()) return;
if (f(*this->begin())) return;
(range_t&)*this = range( std::next(this->begin()), this->end() );
}
}
};
template
struct filter_psuedo_iterator {
using iterator_category=std::input_iterator_tag;
filter_helper* helper = nullptr;
bool m_is_end = true;
bool is_end() const {
return m_is_end || !helper || helper->empty();
}
void operator++() {
helper->advance();
}
typename std::iterator_traits::reference
operator*() const {
return *(helper->begin());
}
It base() const {
if (!helper) return {};
if (is_end()) return helper->end();
return helper->begin();
}
friend bool operator==(filter_psuedo_iterator const& lhs, filter_psuedo_iterator const& rhs) {
if (lhs.is_end() && rhs.is_end()) return true;
if (lhs.is_end() || rhs.is_end()) return false;
return lhs.helper->begin() == rhs.helper->begin();
}
friend bool operator!=(filter_psuedo_iterator const& lhs, filter_psuedo_iterator const& rhs) {
return !(lhs==rhs);
}
};
template
struct filter_range:
private filter_helper,
range_t>
{
using helper=filter_helper;
using range=range_t>;
using range::begin; using range::end; using range::empty;
filter_range( range_t r, F f ):
helper{{r}, std::forward(f)},
range{ {this, false}, {this, true} }
{}
};
template
auto filter( F&& f ) {
return [f=std::forward(f)](auto&& r)
{
using std::begin; using std::end;
using iterator = decltype(begin(r));
return filter_range>{
range(begin(r), end(r)), f
};
};
};
I took short cuts. A real library should make real iterators, not the for(:)
-qualifying pseudo-fascades I did.
At point of use, it looks like this:
int main()
{
std::vector test = {1,2,3,4,5};
for( auto i: filter([](auto x){return x%2;})( test ) )
std::cout << i << '\n';
}
which is pretty nice, and prints
1
3
5
Live example.
There is a proposed addition to C++ called Rangesv3 which does this kind of thing and more. boost
also has filter ranges/iterators available. boost also has helpers that make writing the above much shorter.