Rangeの直積を作るadaptor
直積を作るadaptorがない気がしたので書いてみました。
つかいかた
#include <iostream> #include <string> #include <boost/lambda/lambda.hpp> #include <boost/range/algorithm/for_each.hpp> #include <boost/range/irange.hpp> #include <boost/tuple/tuple_io.hpp> #include "product.hpp" int main() { // (1 a) (1 b) (1 c) (2 a) (2 b) (2 c) (3 a) (3 b) (3 c) boost::for_each( boost::irange(1, 4) | iorate::product(boost::irange('a', 'd')), std::cout << boost::lambda::_1 << ' ' ); }
書いている途中で oven::comprehension の存在を知りました。
http://d.hatena.ne.jp/uskz/20080917/p4
Ovenは偉大だということですね。
せっかくなので、こちらはBoost.PPにより多数のrangeを扱うことができ、また元のrangeのtraversalに合わせるようなものにしました。
product.hpp
#ifndef BOOST_PP_IS_ITERATING #ifndef IORATE_PRODUCT_HPP_INCLUDED #define IORATE_PRODUCT_HPP_INCLUDED #include <boost/iterator/iterator_categories.hpp> #include <boost/iterator/iterator_facade.hpp> #include <boost/iterator/iterator_traits.hpp> #include <boost/mpl/deref.hpp> #include <boost/mpl/min_element.hpp> #include <boost/mpl/placeholders.hpp> #include <boost/mpl/vector.hpp> #include <boost/preprocessor/arithmetic.hpp> #include <boost/preprocessor/cat.hpp> #include <boost/preprocessor/repetition.hpp> #include <boost/range/begin.hpp> #include <boost/range/end.hpp> #include <boost/range/iterator_range.hpp> #include <boost/tuple/tuple.hpp> #include <boost/type_traits/is_convertible.hpp> #ifndef IORATE_PRODUCT_MAX_ARITY #define IORATE_PRODUCT_MAX_ARITY 10 #endif namespace iorate { #define BOOST_PP_ITERATION_LIMITS (2, IORATE_PRODUCT_MAX_ARITY) #define BOOST_PP_FILENAME_1 "product.hpp" #include BOOST_PP_ITERATE() } #endif // IORATE_PRODUCT_HPP_INCLUDED #else // BOOST_PP_IS_ITERATING #define n BOOST_PP_ITERATION() namespace detail { #define PRODUCT_iterator BOOST_PP_CAT(product_iterator, n) template <BOOST_PP_ENUM_PARAMS(n, class Iterator)> struct PRODUCT_iterator; #define PRODUCT_trait(z, m, trait) \ typename boost::BOOST_PP_CAT(iterator_, trait)< \ BOOST_PP_CAT(Iterator, m) \ >::type template <BOOST_PP_ENUM_PARAMS(n, class Iterator)> struct BOOST_PP_CAT(product_iterator_super, n) { typedef boost::iterator_facade< PRODUCT_iterator<BOOST_PP_ENUM_PARAMS(n, Iterator)>, boost::tuple<BOOST_PP_ENUM(n, PRODUCT_trait, value)>, typename boost::mpl::deref< typename boost::mpl::min_element< boost::mpl::vector<BOOST_PP_ENUM(n, PRODUCT_trait, traversal)>, boost::is_convertible<boost::mpl::_2, boost::mpl::_1> >::type >::type, boost::tuple<BOOST_PP_ENUM(n, PRODUCT_trait, value)> > type; }; #undef PRODUCT_trait template <BOOST_PP_ENUM_PARAMS(n, class Iterator)> struct PRODUCT_iterator : BOOST_PP_CAT(product_iterator_super, n)< BOOST_PP_ENUM_PARAMS(n, Iterator) >::type { private: typedef typename BOOST_PP_CAT(product_iterator_super, n)< BOOST_PP_ENUM_PARAMS(n, Iterator) >::type super_t; typedef typename super_t::reference ref_t; typedef typename super_t::difference_type diff_t; public: PRODUCT_iterator() {} #define PRODUCT_parameter(z, m, unused) \ BOOST_PP_CAT(Iterator, m) BOOST_PP_CAT(first, m), \ BOOST_PP_CAT(Iterator, m) BOOST_PP_CAT(last, m), \ BOOST_PP_CAT(Iterator, m) BOOST_PP_CAT(it, m) #define PRODUCT_initializer(z, m, unused) \ BOOST_PP_CAT(m_first, m)(BOOST_PP_CAT(first, m)), \ BOOST_PP_CAT(m_last, m)(BOOST_PP_CAT(last, m)), \ BOOST_PP_CAT(m_it, m)(BOOST_PP_CAT(it, m)) PRODUCT_iterator(BOOST_PP_ENUM(n, PRODUCT_parameter, ~)) : BOOST_PP_ENUM(n, PRODUCT_initializer, ~) {} #undef PRODUCT_initializer #undef PRODUCT_parameter private: #define PRODUCT_member(z, m, unused) \ BOOST_PP_CAT(Iterator, m) \ BOOST_PP_CAT(m_first, m), BOOST_PP_CAT(m_last, m), BOOST_PP_CAT(m_it, m); BOOST_PP_REPEAT(n, PRODUCT_member, ~) #undef PRODUCT_member #define PRODUCT_short_size(z, l, unused) \ * (BOOST_PP_CAT(m_last, l) - BOOST_PP_CAT(m_first, l)) #define PRODUCT_size(m) \ (1 BOOST_PP_REPEAT_FROM_TO(BOOST_PP_INC(m), n, PRODUCT_short_size, ~)) diff_t get_position() const { #define PRODUCT_get(z, m, unused) \ + (BOOST_PP_CAT(m_it, m) - BOOST_PP_CAT(m_first, m)) * PRODUCT_size(m) return 0 BOOST_PP_REPEAT(n, PRODUCT_get, ~); #undef PRODUCT_get } void set_position(diff_t position) { #define PRODUCT_set(z, m, unused) \ BOOST_PP_CAT(m_it, m) += position / PRODUCT_size(m); \ position %= PRODUCT_size(m); BOOST_PP_REPEAT(n, PRODUCT_set, ~) #undef PRODUCT_set } #undef PRODUCT_size #undef PRODUCT_short_size friend class boost::iterator_core_access; ref_t dereference() const { return ref_t(BOOST_PP_ENUM_PARAMS(n, *m_it)); } bool equal(PRODUCT_iterator const &other) const { #define PRODUCT_equal(z, m, unused) \ && BOOST_PP_CAT(m_it, m) == BOOST_PP_CAT(other.m_it, m) return true BOOST_PP_REPEAT(n, PRODUCT_equal, ~); #undef PRODUCT_equal } void increment() { #define PRODUCT_increment(z, m, unused) \ if (++BOOST_PP_CAT(m_it, BOOST_PP_SUB(n, m)) == \ BOOST_PP_CAT(m_last, BOOST_PP_SUB(n, m))) { #define PRODUCT_assign(z, m, unused) \ BOOST_PP_CAT(m_it, m) = BOOST_PP_CAT(m_first, m); \ } BOOST_PP_REPEAT_FROM_TO(1, n, PRODUCT_increment, ~) ++m_it0; BOOST_PP_REPEAT_FROM_TO(1, n, PRODUCT_assign, ~) #undef PRODUCT_assign #undef PRODUCT_increment } void decrement() { #define PRODUCT_equal(z, m, unused) \ if (BOOST_PP_CAT(m_it, BOOST_PP_SUB(n, m)) == \ BOOST_PP_CAT(m_first, BOOST_PP_SUB(n, m))) { #define PRODUCT_decrement(z, m, unused) \ BOOST_PP_CAT(m_it, m) = BOOST_PP_CAT(m_last, m); \ }\ --BOOST_PP_CAT(m_it, m); BOOST_PP_REPEAT_FROM_TO(1, n, PRODUCT_equal, ~) --m_it0; BOOST_PP_REPEAT_FROM_TO(1, n, PRODUCT_decrement, ~) #undef PRODUCT_decrement #undef PRODUCT_equal } void advance(diff_t diff) { set_position(get_position() + diff); } diff_t distance_to(PRODUCT_iterator const &other) const { return other.get_position() - get_position(); } }; } // namespace detail namespace product_detail { template <BOOST_PP_ENUM_PARAMS(n, class Range)> struct BOOST_PP_CAT(base, n) { #define PRODUCT_const_iterator(z, m, unused) \ typename boost::range_const_iterator<BOOST_PP_CAT(Range, m)>::type typedef detail::PRODUCT_iterator< BOOST_PP_ENUM(n, PRODUCT_const_iterator, ~) > iter_t; #undef PRODUCT_const_iterator typedef boost::iterator_range<iter_t> result_type; result_type operator()(BOOST_PP_ENUM_BINARY_PARAMS(n, Range, const &rng)) const { #define PRODUCT_parameter(z, m, unused) \ boost::begin(BOOST_PP_CAT(rng, m)), boost::end(BOOST_PP_CAT(rng, m)) return aux(BOOST_PP_ENUM(n, PRODUCT_parameter, ~)); #undef PRODUCT_parameter } #define PRODUCT_parameter(z, m, unused) \ BOOST_PP_CAT(Iterator, m) BOOST_PP_CAT(first, m), \ BOOST_PP_CAT(Iterator, m) BOOST_PP_CAT(last, m) template <BOOST_PP_ENUM_PARAMS(n, class Iterator)> result_type aux(BOOST_PP_ENUM(n, PRODUCT_parameter, ~)) const { #undef PRODUCT_parameter #define PRODUCT_parameter(z, m, unused) \ BOOST_PP_CAT(first, m), BOOST_PP_CAT(last, m), BOOST_PP_CAT(first, m) return result_type( iter_t(BOOST_PP_ENUM(n, PRODUCT_parameter, ~)), iter_t( first0, last0, last0, BOOST_PP_ENUM_SHIFTED(n, PRODUCT_parameter, ~) ) ); #undef PRODUCT_parameter } }; #define PRODUCT_adaptor BOOST_PP_CAT(product_adaptor, n) template <BOOST_PP_ENUM_SHIFTED_PARAMS(n, class Range)> struct PRODUCT_adaptor { #define PRODUCT_initializer(z, m, unused) \ BOOST_PP_CAT(m_rng, m)(BOOST_PP_CAT(rng, m)) explicit PRODUCT_adaptor( BOOST_PP_ENUM_SHIFTED_BINARY_PARAMS(n, Range, const &rng) ) : BOOST_PP_ENUM_SHIFTED(n, PRODUCT_initializer, ~) {} #undef PRODUCT_initializer #define PRODUCT_member(z, m, unused) \ BOOST_PP_CAT(Range, m) const &BOOST_PP_CAT(m_rng, m); BOOST_PP_REPEAT_FROM_TO(1, n, PRODUCT_member, ~) #undef PRODUCT_member }; template <BOOST_PP_ENUM_PARAMS(n, class Range)> typename BOOST_PP_CAT(base, n)<BOOST_PP_ENUM_PARAMS(n, Range)>::result_type operator|( Range0 const &rng0, PRODUCT_adaptor<BOOST_PP_ENUM_SHIFTED_PARAMS(n, Range)> adaptor ) { return BOOST_PP_CAT(base, n)<BOOST_PP_ENUM_PARAMS(n, Range)>()( rng0, BOOST_PP_ENUM_SHIFTED_PARAMS(n, adaptor.m_rng) ); } } // namespace product_detail template <BOOST_PP_ENUM_SHIFTED_PARAMS(n, class Range)> product_detail::PRODUCT_adaptor<BOOST_PP_ENUM_SHIFTED_PARAMS(n, Range)> product(BOOST_PP_ENUM_SHIFTED_BINARY_PARAMS(n, Range, const &rng)) { return product_detail::PRODUCT_adaptor<BOOST_PP_ENUM_SHIFTED_PARAMS(n, Range)>( BOOST_PP_ENUM_SHIFTED_PARAMS(n, rng) ); } #undef PRODUCT_adaptor #undef PRODUCT_iterator #undef n #endif // BOOST_PP_IS_ITERATING
途中で boost::common_type を使おうと思ったのですが、VC10では3引数までしか受け付けてくれなかったので、代わりにMPLを使いました。