sequence.hpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2014, 2015 Ableton AG, Berlin. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21 //
22 
27 #pragma once
28 
29 #include <atria/xform/reductor.hpp>
30 #include <atria/xform/meta.hpp>
33 #include <ableton/build_system/Warnings.hpp>
34 ABL_DISABLE_WARNINGS
35 #include <boost/range/iterator_range.hpp>
36 #include <boost/iterator/iterator_facade.hpp>
37 #include <boost/optional.hpp>
38 #include <boost/mpl/eval_if.hpp>
39 ABL_RESTORE_WARNINGS
40 #include <vector>
41 #include <iostream>
42 
43 namespace atria {
44 namespace xform {
45 
46 namespace detail {
47 
48 template <typename ValueT, typename XformT, typename... IteratorTs>
49 struct sequence_data
50 {
51  using value_t = ValueT;
52  using cache_t = std::vector<value_t>;
53  using state_t = sequence_data*;
54 
55  struct step_t
56  {
57  template <typename... Ts>
58  state_t operator() (state_t s, Ts&& ...xs)
59  {
60  s->impl_.cache.emplace_back(std::forward<Ts>(xs)...);
61  return s;
62  }
63  };
64 
65  using reductor_t = reductor_fn<
66  estd::decay_t<estd::result_of_t<XformT(step_t)> >,
67  state_t,
68  meta::value_t<IteratorTs>... >;
69 
70  template <typename... RangeTs>
71  sequence_data(XformT xform, const RangeTs& ...ranges)
72  : impl_ {
73  std::size_t {},
74  cache_t {},
75  reductor_t {
76  std::move(xform)(step_t{}),
77  this,
78  *std::begin(ranges)...},
79  std::make_tuple(++std::begin(ranges)...),
80  std::make_tuple(std::end(ranges)...)
81  }
82  {
83  pull();
84  }
85 
86  sequence_data(sequence_data&& other)
87  : impl_(std::move(other.impl_))
88  { impl_.reductor.current(this); }
89 
90  sequence_data(const sequence_data& other)
91  : impl_(other.impl_)
92  { impl_.reductor.current(this); }
93 
94  sequence_data& operator=(sequence_data&& other)
95  {
96  impl_ = std::move(other.impl);
97  impl_.reductor.current(this);
98  }
99 
100  sequence_data& operator=(const sequence_data& other)
101  {
102  using std::swap;
103  sequence_data copied{ other };
104  swap(*this, copied);
105  return *this;
106  }
107 
108  bool empty() const
109  {
110  return impl_.pos == impl_.cache.size() && (
111  !impl_.reductor ||
112  !detail::tuple_all_neq(impl_.firsts, impl_.lasts));
113  }
114 
115  const value_t& front() const
116  {
117  return impl_.cache[impl_.pos];
118  }
119 
120  void pop()
121  {
122  ++ impl_.pos;
123  pull();
124  }
125 
126  bool operator==(const sequence_data& other) const
127  {
128  return impl_.firsts == other.impl_.firsts
129  && impl_.pos == other.impl_.pos;
130  }
131 
132  bool operator!=(const sequence_data& other) const
133  { return !(*this == other); }
134 
135 private:
136  void pull()
137  { pull(estd::make_index_sequence<sizeof...(IteratorTs)>{}); }
138 
139  template <std::size_t... Indexes>
140  void pull(estd::index_sequence<Indexes...>)
141  {
142  if (impl_.pos == impl_.cache.size())
143  {
144  impl_.cache.clear();
145  impl_.pos = 0;
146  while (impl_.cache.empty() &&
147  impl_.reductor &&
148  detail::tuple_all_neq(impl_.firsts, impl_.lasts))
149  {
150  using std::get;
151  impl_.reductor(*get<Indexes>(impl_.firsts)...);
152  meta::noop(++get<Indexes>(impl_.firsts)...);
153  }
154  }
155  }
156 
157  struct {
158  std::size_t pos;
159  cache_t cache;
160  reductor_t reductor;
161  std::tuple<IteratorTs...> firsts;
162  std::tuple<IteratorTs...> lasts;
163  } impl_;
164 };
165 
166 } // namespace detail
167 
187 template <typename ValueT,
188  typename XformT,
189  typename... RangeTs>
191 {
192  using data_t = detail::sequence_data<
193  ValueT,
194  XformT,
195  decltype(std::begin(std::declval<const RangeTs&>()))...>;
196 
197  using value_type = const ValueT;
198 
199  struct iterator : boost::iterator_facade<iterator,
200  value_type,
201  boost::forward_traversal_tag>
202  {
203  private:
204  friend sequence_range;
205  friend class boost::iterator_core_access;
206 
207  iterator(boost::optional<data_t> data)
208  : data_(data && !data->empty() ? data : boost::none)
209  {}
210 
211  void increment()
212  {
213  data_->pop();
214  if (data_->empty())
215  data_ = boost::none;
216  }
217 
218  bool equal(const iterator& other) const
219  { return data_ == other.data_; }
220 
221  const value_type& dereference() const
222  { return data_->front(); }
223 
224  boost::optional<data_t> data_;
225  };
226 
227  sequence_range(XformT xform, const RangeTs& ...ranges)
228  : xform_(std::move(xform))
229  , ranges_(ranges...)
230  {}
231 
232  using const_iterator = iterator;
233 
234  iterator begin() const { return { make_data() }; }
235  iterator end() const { return { boost::none }; }
236 
237 private:
238  boost::optional<data_t> make_data() const
239  { return make_data(estd::make_index_sequence<sizeof...(RangeTs)>{}); }
240 
241  template <std::size_t... Indexes>
242  boost::optional<data_t> make_data(estd::index_sequence<Indexes...>) const
243  {
244  return detail::tuple_all_neq(
245  std::make_tuple(std::begin(std::get<Indexes>(ranges_))...),
246  std::make_tuple(std::end(std::get<Indexes>(ranges_))...))
247  ? boost::make_optional(data_t { xform_, std::get<Indexes>(ranges_)... })
248  : boost::none;
249  }
250 
251  XformT xform_;
252  std::tuple<const RangeTs&...> ranges_;
253 };
254 
255 namespace detail {
256 struct deduce_value_type {};
257 } // namespace detail
258 
265 template <typename ValueT = detail::deduce_value_type,
266  typename XformT,
267  typename... RangeTs>
268 auto sequence(XformT&& xform, const RangeTs&... ranges)
269  -> sequence_range<
270  typename boost::mpl::eval_if<
271  std::is_same<ValueT, detail::deduce_value_type>,
273  meta::identity<ValueT> >::type,
276 {
277  return { std::forward<XformT>(xform), ranges... };
278 }
279 
280 } // namespace xform
281 } // namespace atria
constexpr bool operator!=(const pack< Ts1... > &, const pack< Ts2... > &)
Two packs are different if they are of different types.
Definition: pack.hpp:74
eval_t< get_value_type< estd::decay_t< T > > > value_t
Convenient alias for get_value_type
Definition: value_type.hpp:76
Definition: pack.hpp:121
STL namespace.
typename std::decay< T >::type decay_t
Similar to C++14 std::decay_t.
Definition: type_traits.hpp:54
Metafunction that given a transducer XformT and some inputs InputTs, returns the type of the output o...
Definition: meta.hpp:81
Identity metafunction.
Definition: utils.hpp:36
Range adaptor that transduces the ranges in RangeTs with the transducer XformT, producing values of V...
Definition: sequence.hpp:190
C++ amazing templates and reusable implementations awesomeness.
Definition: _doc.hpp:35
auto sequence(XformT &&xform, const RangeTs &...ranges) -> sequence_range< typename boost::mpl::eval_if< std::is_same< ValueT, detail::deduce_value_type >, result_of< XformT, meta::value_t< RangeTs >... >, meta::identity< ValueT > >::type, estd::decay_t< XformT >, estd::decay_t< RangeTs >... >
Factory for sequence_range values producing an iterable range out of a transducer, in the spirit of clojure.core/sequence$2.
Definition: sequence.hpp:268
auto reductor(ReducingFnT &&step, InitialStateT &&state, InputTs &&...ins) -> reductor_fn< estd::decay_t< ReducingFnT >, estd::decay_t< InitialStateT >, estd::decay_t< InputTs >... >
Constructs a reductor_fn object with deduced argument types.
Definition: reductor.hpp:175
Fork me on GitHub