All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Macros Pages
combine_iterator.hpp
Go to the documentation of this file.
1 //=======================================================================
2 // Copyright (c) 2013 Piotr Wygocki
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See
5 // accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 //=======================================================================
17 
18 #include <boost/iterator/filter_iterator.hpp>
19 #include <boost/iterator/transform_iterator.hpp>
20 #include <boost/iterator/iterator_facade.hpp>
21 #include <boost/range/empty.hpp>
22 
23 #ifndef PAAL_COMBINE_ITERATOR_HPP
24 #define PAAL_COMBINE_ITERATOR_HPP
25 
26 namespace paal {
27 namespace data_structures {
28 //TODO change name to product
34 template <typename... Ranges> class combine_iterator_engine;
35 
43 template <typename Range, typename... RangesRest>
45  Range, RangesRest...> : private combine_iterator_engine<RangesRest...> {
46 
47  public:
48  using base = combine_iterator_engine<RangesRest...>;
49  using Iterator = typename boost::range_iterator<Range>::type;
50 
57  combine_iterator_engine(Range &range, RangesRest &... rest)
58  : base(rest...), m_begin(std::begin(range)), m_curr(std::begin(range)),
59  m_end(std::end(range)) {}
60 
61  combine_iterator_engine() = default;
62 
68  bool next() {
69  if (!base::next()) {
70  ++m_curr;
71  if (m_curr == m_end) {
72  m_curr = m_begin;
73  return false;
74  }
75  }
76  return true;
77  }
78 
89  template <typename F, typename... Args>
90  auto call(F f, Args &&... args)->decltype(std::declval<base>().call(
91  std::move(f), std::forward<Args>(args)..., *std::declval<Iterator>())) {
92  return base::call(std::move(f), std::forward<Args>(args)..., *m_curr);
93  }
94 
103  friend bool operator==(const combine_iterator_engine &left,
104  const combine_iterator_engine &right) {
105  return left.m_begin == right.m_begin && left.m_end == right.m_end &&
106  left.m_curr == right.m_curr &&
107  static_cast<base>(left) == static_cast<base>(right);
108  }
109 
110  private:
111  Iterator m_begin;
112  Iterator m_curr;
113  Iterator m_end;
114 };
115 
119 template <> class combine_iterator_engine<> {
120  public:
126  bool next() { return false; }
127 
138  template <typename F, typename... Args>
139  auto call(F f, Args &&... args)->decltype(f(std::forward<Args>(args)...)) {
140  return f(std::forward<Args>(args)...);
141  }
142 
151  friend bool operator==(const combine_iterator_engine &left,
152  const combine_iterator_engine &right) {
153  return true;
154  }
155 };
156 
157 namespace detail {
158 // TODO can you do this without alias???
159 template <typename T> using rem_ref = typename std::remove_reference<T>::type;
160 }
161 
170 template <typename... Ranges>
171 combine_iterator_engine<detail::rem_ref<Ranges>...>
172 make_combine_iterator_engine(Ranges &&... ranges) {
173  // see comments in make_combine_iterator
174  return combine_iterator_engine<detail::rem_ref<Ranges>...>{ ranges... };
175 }
176 
185 template <typename Joiner, typename... Ranges>
186 class combine_iterator : public boost::iterator_facade<
187  combine_iterator<Joiner, Ranges...>,
188  puretype(combine_iterator_engine<Ranges...>().call(std::declval<Joiner>())),
189  boost::forward_traversal_tag // TODO this should be minimal tag of the
190  // ranges
191  ,
192  decltype(
193  combine_iterator_engine<Ranges...>().call(std::declval<Joiner>()))> {
194  public:
201  combine_iterator(Joiner joiner, Ranges &... ranges)
202  : m_joiner(joiner), m_iterator_engine(ranges...),
203  m_end(sizeof...(Ranges) ? is_empty(ranges...) : true) {}
204 
208  combine_iterator() : m_end(true) {};
209 
210  private:
221  template <typename Range, typename... RangesRest>
222  bool is_empty(const Range &range, const RangesRest &... rest) {
223  if (boost::empty(range)) {
224  return true;
225  } else {
226  return is_empty(rest...);
227  }
228  }
229 
235  bool is_empty() { return false; }
236 
237  using ref = decltype(
238  combine_iterator_engine<Ranges...>().call(std::declval<Joiner>()));
239 
240  friend class boost::iterator_core_access;
241 
245  void increment() {
246  if (!m_iterator_engine.next()) {
247  m_end = true;
248  }
249  }
250 
258  bool equal(combine_iterator const &other) const {
259  return this->m_end == other.m_end &&
260  (this->m_end ||
261  this->m_iterator_engine == other.m_iterator_engine);
262  }
263 
269  ref dereference() const { return m_iterator_engine.call(m_joiner); }
270 
271  Joiner m_joiner;
272  mutable combine_iterator_engine<Ranges...> m_iterator_engine;
273  bool m_end;
274 };
275 
286 template <typename Joiner, typename... Ranges>
287 combine_iterator<Joiner, detail::rem_ref<Ranges>...>
288 make_combine_iterator(Joiner joiner, Ranges &&... ranges) {
289  // we do not forward the ranges, because combine_iterator expects lvalues
290  // we Use Ranges && because, we'd like to cover const/nonconst cases
292  ranges... };
293 }
294 
295 } // data_structures
296 } // paal
297 
298 #endif // PAAL_COMBINE_ITERATOR_HPP
combine_iterator iterates through all combinations of values from given ranges and returns them joine...
combine_iterator()
default constructor represents end of the range
friend bool operator==(const combine_iterator_engine &left, const combine_iterator_engine &right)
operator==, always true
class representing set of ranges with two operation next and call
combine_iterator_engine(Range &range, RangesRest &...rest)
constructor
auto call(F f, Args &&...args) -> decltype(f(std::forward< Args >(args)...))
actually calls function f
combine_iterator(Joiner joiner, Ranges &...ranges)
constructor
combine_iterator< Joiner, detail::rem_ref< Ranges >...> make_combine_iterator(Joiner joiner, Ranges &&...ranges)
make for combine_iterator
auto call(F f, Args &&...args) -> decltype(std::declval< base >().call(std::move(f), std::forward< Args >(args)...,*std::declval< Iterator >()))
calls arbitrary function f on (*m_curr)...
combine_iterator_engine< detail::rem_ref< Ranges >...> make_combine_iterator_engine(Ranges &&...ranges)
make for combine_iterator_engine
friend bool operator==(const combine_iterator_engine &left, const combine_iterator_engine &right)
operator==