A while ago I wrote up a little demo of using lambdas to implement convenient lazy list comprehension in C++0x, but at the time the VC++ compiler I was using lacked decltype
, and I found a need for it.
I just tried out the Beta 1 that was released this week, and it has decltype
, so here's the updated sample:
#include <iostream> #include <vector> #include <sstream> // Some helpers for working with decltype // (maybe these are in std:: somewhere?) template <class T> T *make_ptr() { return (T *)0; } template <class T> const T &make_ref() { return *(make_ptr<T>()); } // so you don't need to obtain boost::lexical_cast! template <class T> std::string to_string(const T &v) { std::ostringstream s; s << v; return s.str(); } // Unary function that always returns true template <class T> struct always_true { bool operator() (const T &) const { return true; } }; // Unary function that returns its argument template <class T> struct pass_thru { T operator() (const T &e) const { return e; } }; template <class TElemFrom, class TElemTo, class TIterFrom, class TSelector, class TPredicate> class filter { TIterFrom from_; TIterFrom end_; const TSelector &selector_; const TPredicate &predicate_; public: filter(TIterFrom from, TIterFrom end, const TSelector &selector, const TPredicate &predicate) : from_(from), end_(end), selector_(selector), predicate_(predicate) {} class const_iterator { TIterFrom from_; TIterFrom end_; const TSelector &selector_; const TPredicate &predicate_; void locate() { while (!done()) { if (predicate_(*from_)) return; ++from_; } } bool done() const { return (from_ == end_); } public: const_iterator(TIterFrom from, TIterFrom end, const TSelector &selector, const TPredicate &predicate) : from_(from), end_(end), selector_(selector), predicate_(predicate) { locate(); } TElemTo operator*() const { return selector_(*from_); } const_iterator operator++() { ++from_; locate(); return *this; } bool operator==(const const_iterator &other) const { return done() && other.done(); } bool operator!=(const const_iterator &other) const { return !done() || !other.done(); } }; typedef TElemFrom value_type; const_iterator begin() const { return const_iterator(from_, end_, selector_, predicate_); } const_iterator end() const { return const_iterator(end_, end_, selector_, predicate_); } template <class TSelector> filter<TElemTo, decltype(make_ref<TSelector>()(make_ref<TElemTo>())), const_iterator, TSelector, always_true<TElemTo> > select(const TSelector &selector) { return filter<TElemTo, decltype(make_ref<TSelector>()(make_ref<TElemTo>())), const_iterator, TSelector, always_true<TElemTo> > (begin(), end(), selector, always_true<TElemTo>()); } template <class TPredicate> filter<TElemTo, TElemTo, const_iterator, pass_thru<TElemTo>, TPredicate> where(const TPredicate &predicate) { return filter<TElemTo, TElemTo, const_iterator, pass_thru<TElemTo>, TPredicate> (begin(), end(), pass_thru<TElemTo>(), predicate); } }; template <class TCollFrom> filter<typename TCollFrom::value_type, typename TCollFrom::value_type, typename TCollFrom::const_iterator, pass_thru<typename TCollFrom::value_type>, always_true<typename TCollFrom::value_type> > from(const TCollFrom &from) { return filter<typename TCollFrom::value_type, typename TCollFrom::value_type, typename TCollFrom::const_iterator, pass_thru<typename TCollFrom::value_type>, always_true<typename TCollFrom::value_type> > (from.begin(), from.end(), pass_thru<typename TCollFrom::value_type>(), always_true<typename TCollFrom::value_type>()); } int main(int argc, char* argv[]) { std::vector<int> vecInts; vecInts.push_back(5); vecInts.push_back(2); vecInts.push_back(9); auto filtered = from(vecInts) .where([] (int n) { return n > 3; }) .select([] (int n) { return "\"" + to_string(n) + "\""; }); for (auto i = filtered.begin(); i != filtered.end(); ++i) std::cout << *i << std::endl; return 0; }
The main function is the pay-off, of course. I have a vector of ints
, and I wrap it something that ends up stored in a variable called filtered, which I can then iterate through. The filtering (discarding anything not greater than 3) and transforming (int to quoted string) of the items happens on-the-fly during that iteration.
(And to clarify, this is different from the last time I posted it because select
no longer requires any type parameters to be explicitly given - thanks to decltype
).