// Copyright 2008 Giovani P. Deretta. Use, modification and distribution is // subject to the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include #include #include #include #include #include #include #include #include #include #include /* Using deforestation rules in an attempt to reduce abstraction overhead of boost iterator adapters. Without any defines, sequences of map and filter won't be reduced. It is left to the compiler to optimize the code and reduce abstraction overhead. - Define PLAIN_FOR to test baseline explicit for based loop. This is the theoretical best code attainable. - Define DO_REWRITE to enable rewrite rules to simplify sequences of filter/map. See below for exact rewrite rules. - Define DO_TRAVERSAL to also enable optimized fold/for_each. Results: surprisingly, with gcc-4.x I cannot measure any significant abstraction penalities by using even long chains of {transform,filter}_iterators in integer based computations. Enabling rewrite rules or traversal doesn't change anything. Gcc-3.3, OTOH, has a larger abstraction overhead and the benefit of both rewrite and traversal is around 20% (~10% each). Note: the test code uses typeof for simplicity. It should be easy to remove it to test on compilers which do not have this functionality. TODO: test abstraction penality on more complex code (not just integers), or with more complex mappers and filters. TODO: add more iterators (unfold, zip, take) and more rewrite rules. */ namespace mpl =boost::mpl; namespace xrange { namespace detail { // Why not use boost::range? We need metafunctions that // return const_iterators and iterators according to the // {lvalue/const lvalue/rvalue}ness of their parameters. We // might as well put everithing we need here. template struct range_iterator { typedef typename Range::const_iterator type; }; template struct range_iterator { typedef typename Range::iterator type; }; template typename range_iterator::type begin(R const&r) { return r.begin(); } template typename range_iterator::type begin(R&r) { return r.begin(); } template typename range_iterator::type end(const R&r) { return r.end(); } template typename range_iterator::type end(R&r) { return r.end(); } template struct range_iterator : range_iterator {}; template struct range_value : boost::iterator_value::type> {}; template struct range_reference : boost::iterator_reference::type> {}; template struct result_of : boost::result_of {}; template struct remove_rcv : boost::mpl::apply< boost::remove_cv > , T > {}; template struct make_transform_iterator : mpl::apply< boost::transform_iterator< remove_rcv, range_iterator, result_of< remove_rcv, boost::iterator_reference< range_iterator > > > , R, M >{}; template struct make_filter_iterator : mpl::apply, range_iterator >, R, F> {}; // compose(A, B)(x) -> A(B(x)) template struct compose { compose(F1 f1_, F2 f2_) : f1(f1_), f2(f2_) {} template struct result; template struct result : mpl::apply< detail::result_of< F1, detail::result_of< F1, mpl::_1 > >, P> {}; template typename result::type operator()(P const& p) const { return f1(f2(p)); } template typename result::type operator()(P& p) const { return f1(f2(p)); } F1 f1; F2 f2; }; template struct and_ { and_(A a_, B b_): a(a_), b(b_) {} typedef bool result_type; template bool operator()(X const& x) const { return a(x) && b(x); } A a; B b; }; } template struct map_range { map_range(R const&r_, M m_) : r(r_), m(m_) {} typedef typename detail::make_transform_iterator::type iterator; typedef iterator const_iterator; iterator begin() const { return iterator(detail::begin(r), m); } iterator end() const { return iterator(detail::end(r), m); } R r; M m; }; template struct filter_range { filter_range(R const&r_, P p_) : r(r_), p(p_) {} typedef typename detail::make_filter_iterator::type iterator; typedef iterator const_iterator; iterator begin() const { return iterator(p, detail::begin(r), detail::end(r)); } iterator end() const { return iterator(p, detail::end(r), detail::end(r)); } R r; P p; }; ///// Rewrite rules: // base cases: identity template struct map_range_r { typedef map_range type; type operator()(R const& r, M m) { return type(r, m); } }; template struct filter_range_r { typedef filter_range type; type operator()(R const& r, P f) { return type(r, f); }; }; #if defined(DO_REWRITE) // map(map(r, a), b) -> map(r, \x->a(b(x))) template struct map_range_r, M2> { typedef map_range_r > super; typedef typename super::type type; type operator()(map_range r, M2 m) { return super()(r.r, detail::compose(m, r.m)); } }; // filter(filter(r, a), b) -> filter(r, \x->a(x) && b(x)) template struct filter_range_r, P2> { typedef filter_range_r > super; typedef typename super::type type; type operator()(filter_range r, P2 p) { return super()(r.r, detail::and_(r.p, p)); } }; // filter(map(r, a), b) -> map(filter(r, b), \x->a(b(x))) template struct filter_range_r, P> { typedef filter_range_r > filter; typedef map_range type; type operator()(map_range r, P p) { return type(filter()(r.r, detail::compose(p, r.m)), r.m); } }; #endif /// note: filter and map by default return a lazy range that closes around its arguments by value. /// use boost::sub_range to prevent this. /// todo: map and filter should close by reference by default except for lightweight ranges. // workaround: using map_ to prevent adl collision with std::map template typename map_range_r::type map_(R const& r, M m) { return map_range_r()(r, m); } template typename map_range_r::type map(R const& r, M m) { return map_(r, m); } template typename filter_range_r::type filter(R const& r, F m) { return filter_range_r()(r, m); } template T accumulate(R const r, T x, F f) { return std::accumulate(detail::begin(r), detail::end(r), x, f); } #ifdef DO_TRAVERSAL // Generic traversal: // for every i in [begin, end), such as p(i) is true, call // f(m(i)). Early exit if c(i) is false. // todo: add support for Take and zip. template void traversal(I begin, I end, F& f, P& p, M& m, C& c) { for(; (begin != end) /*&& c(*begin)*/; ++begin) { if(p(*begin)) f(m(*begin)); } } struct true_ { template bool operator()(T const&) { return true; } }; template F for_each(map_range, M> r, F f) { true_ t; //typedef typeof(r.r) r_t; traversal(detail::begin(r.r.r), detail::end(r.r.r), f, r.r.p, r.m, t); return f; } template struct accumulator { template void operator()(T const& x) { z = f(z, x); } Z z; F f; }; template T accumulate(map_range, M> r, T x, F f) { accumulator acc = { x, f }; return for_each(r, acc).z; } #endif template F for_each(R const r, F f) { return std::for_each(detail::begin(r), detail::end(r), f); } } // A bounch of mappers and filters for testing. // todo: test more complex mappers and filters struct square { typedef unsigned long result_type; unsigned long operator()(unsigned long x) const { return x*x; } }; template struct times{ typedef unsigned long result_type; unsigned long operator()(unsigned long x) const { return x*I; } }; template struct mod{ typedef bool result_type; bool operator()(unsigned long x) const { return x % I == 0; } }; template struct not_mod{ typedef bool result_type; bool operator()(unsigned long x) const { return x % I != 0; } }; template struct plus { typedef unsigned long result_type; unsigned long operator()(unsigned long x) const { return x + I; } }; template struct less { typedef bool result_type; bool operator()(unsigned long x) const { return x < I; } }; template struct more { typedef bool result_type; bool operator()(unsigned long x) const { return x > I; } }; template struct between { typedef bool result_type; bool operator()(unsigned long x) const { return x > I && x < J; } }; template struct sum { typedef T result_type; template T operator()(T a, T2 b) const { return a + b; } }; #define auto(a, b) typeof(b) a = b template void fail() { T::FAIL(); } void test1() { using namespace xrange; std::vector r ; for (int i=0; i < 1024*1024; ++i) r.push_back(i); auto (sr, boost::sub_range >(r)); auto(r2, filter ( map_ ( filter ( map_ ( map_ ( filter ( map_(sr, times<8>()), less<10000>() ) , times<8>() ), times<8>() ) , mod<2>() ), plus<10>() ), mod<5>() ) ); typedef typeof(r2) r2_t; // uncomment the following line to have the compiler print the type of r2 //fail(); struct each { static void _(int i) { std::cout << i <<':'; }}; //for_each(r2, &each::_); //std::cout <()); #else // base line for based code for timing comparisons. // the compiler should be able to do a good job with this. unsigned long x = 0; for (size_t j = 0; j < 1000; ++j) { for(size_t i = 0; i != r.size(); ++i) { unsigned long k = r[i] * 8; if( k % 2 ) { unsigned long k1 = k*8; unsigned long k2 = k1*8; if(k2 % 5 && (k2 + 10) < 100000) x += k2 + 10; } } } #endif std::cout << x <