HiPipe  0.7.0
C++17 data pipeline with Python bindings.
tuple.hpp
1 /****************************************************************************
2  * hipipe library
3  * Copyright (c) 2017, Cognexa Solutions s.r.o.
4  * Copyright (c) 2018, Iterait a.s.
5  * Author(s) Filip Matzner
6  *
7  * This file is distributed under the MIT License.
8  * See the accompanying file LICENSE.txt for the complete license agreement.
9  ****************************************************************************/
11 
12 #ifndef HIPIPE_CORE_TUPLE_UTILS_HPP
13 #define HIPIPE_CORE_TUPLE_UTILS_HPP
14 
15 #include <range/v3/core.hpp>
16 #include <range/v3/range/conversion.hpp>
17 #include <range/v3/range/primitives.hpp>
18 
19 #include <experimental/tuple>
20 #include <ostream>
21 #include <type_traits>
22 #include <vector>
23 
24 namespace hipipe::utility {
25 
38 template<typename T1, typename T2, typename... Ts>
39 struct variadic_find : std::integral_constant<std::size_t, variadic_find<T1, Ts...>{}+1> {
40 };
41 
42 template<typename T, typename... Ts>
43 struct variadic_find<T, T, Ts...> : std::integral_constant<std::size_t, 0> {
44 };
45 
55 template<std::size_t N, typename... Ts>
56 struct maybe_tuple_impl {
57  using type = std::tuple<Ts...>;
58 };
59 
60 template<typename T>
61 struct maybe_tuple_impl<1, T> {
62  using type = T;
63 };
64 
65 template<typename... Ts>
66 using maybe_tuple = typename maybe_tuple_impl<sizeof...(Ts), Ts...>::type;
67 
76 template<std::size_t Value, std::size_t... Is>
77 constexpr std::index_sequence<(Value + Is)...> plus(std::index_sequence<Is...>)
78 {
79  return {};
80 }
81 
90 template <std::size_t Offset, std::size_t N>
91 using make_offset_index_sequence = decltype(plus<Offset>(std::make_index_sequence<N>{}));
92 
93 // tuple_for_each //
94 
95 namespace detail {
96 
97  template<typename Fun, typename Tuple, std::size_t... Is>
98  constexpr Fun tuple_for_each_impl(Tuple&& tuple, Fun&& fun, std::index_sequence<Is...>)
99  {
100  (..., (std::invoke(fun, std::get<Is>(std::forward<Tuple>(tuple)))));
101  return fun;
102  }
103 
104 } // namespace detail
105 
118 template<typename Tuple, typename Fun>
119 constexpr auto tuple_for_each(Tuple&& tuple, Fun&& fun)
120 {
121  constexpr std::size_t tuple_size = std::tuple_size<std::decay_t<Tuple>>::value;
122  return detail::tuple_for_each_impl(std::forward<Tuple>(tuple),
123  std::forward<Fun>(fun),
124  std::make_index_sequence<tuple_size>{});
125 }
126 
127 // tuple_transform //
128 
129 namespace detail {
130 
131  template<typename Tuple, typename Fun, std::size_t... Is>
132  constexpr auto tuple_transform_impl(Tuple&& tuple, Fun&& fun, std::index_sequence<Is...>)
133  {
134  return std::make_tuple(std::invoke(fun, std::get<Is>(std::forward<Tuple>(tuple)))...);
135  }
136 
137 } // end namespace detail
138 
153 template<typename Tuple, typename Fun>
154 constexpr auto tuple_transform(Tuple&& tuple, Fun&& fun)
155 {
156  constexpr std::size_t tuple_size = std::tuple_size<std::decay_t<Tuple>>::value;
157  return detail::tuple_transform_impl(std::forward<Tuple>(tuple),
158  std::forward<Fun>(fun),
159  std::make_index_sequence<tuple_size>{});
160 }
161 
164 template<typename T, typename Tuple = void>
165 struct tuple_contains;
166 
167 template<typename T, typename... Types>
168 struct tuple_contains<T, std::tuple<Types...>>
169  : std::disjunction<std::is_same<std::decay_t<T>, std::decay_t<Types>>...> {
170 };
171 
174 template<typename Tuple, size_t... Is>
175 std::ostream& tuple_print(std::ostream& out, const Tuple& tuple, std::index_sequence<Is...>)
176 {
177  out << "(";
178  (..., (out << (Is == 0 ? "" : ", ") << std::get<Is>(tuple)));
179  out << ")";
180  return out;
181 }
182 
185 template<typename... Ts>
186 std::ostream& operator<<(std::ostream& out, const std::tuple<Ts...>& tuple)
187 {
188  return utility::tuple_print(out, tuple, std::make_index_sequence<sizeof...(Ts)>{});
189 }
190 
191 // unzip //
192 
193 namespace detail {
194 
195  // wrap each type of a tuple in std::vector, i.e., make a tuple of empty vectors
196  template<typename Tuple, std::size_t... Is>
197  auto vectorize_tuple(std::index_sequence<Is...>)
198  {
199  return std::make_tuple(std::vector<std::tuple_element_t<Is, std::decay_t<Tuple>>>()...);
200  }
201 
202  // push elements from the given tuple to the corresponding vectors in a tuple of vectors
203  template<typename ToR, typename Tuple, std::size_t... Is>
204  void push_unzipped(ToR& tuple_of_ranges, Tuple&& tuple, std::index_sequence<Is...>)
205  {
206  (..., (std::get<Is>(tuple_of_ranges).push_back(std::get<Is>(std::forward<Tuple>(tuple)))));
207  }
208 
209  // if the size of the given range is known, return it, otherwise return 0
210  CPP_template(class Rng)(requires ranges::sized_range<Rng>)
211  std::size_t safe_reserve_size(Rng&& rng)
212  {
213  return ranges::size(rng);
214  }
215  CPP_template(class Rng)(requires !ranges::sized_range<Rng>)
216  std::size_t safe_reserve_size(Rng&& rng)
217  {
218  return 0;
219  }
220 
221  template<typename Rng>
222  auto unzip_impl(Rng& range_of_tuples)
223  {
224  using tuple_type = ranges::range_value_t<Rng>;
225  constexpr auto tuple_size = std::tuple_size<tuple_type>{};
226  constexpr auto indices = std::make_index_sequence<tuple_size>{};
227  std::size_t reserve_size = detail::safe_reserve_size(range_of_tuples);
228 
229  auto tuple_of_ranges = detail::vectorize_tuple<tuple_type>(indices);
231  tuple_of_ranges, [reserve_size](auto& rng) { rng.reserve(reserve_size); });
232 
233  for (auto& v : range_of_tuples) {
234  detail::push_unzipped(tuple_of_ranges, std::move(v), indices);
235  }
236 
237  return tuple_of_ranges;
238  }
239 
240 } // namespace detail
241 
256 CPP_template(class Rng)(requires ranges::range<Rng> && !ranges::view_<Rng>)
257 auto unzip(Rng range_of_tuples)
258 {
259  // copy the given container and move elements out of it
260  return detail::unzip_impl(range_of_tuples);
261 }
262 
264 CPP_template(class Rng)(requires ranges::view_<Rng>)
265 auto unzip(Rng view_of_tuples)
266 {
267  return utility::unzip(ranges::to_vector(view_of_tuples));
268 }
269 
270 // maybe unzip //
271 
272 namespace detail {
273 
274  template<bool Enable>
275  struct unzip_if_impl
276  {
277  template<typename Rng>
278  static decltype(auto) impl(Rng&& rng)
279  {
280  return utility::unzip(std::forward<Rng>(rng));
281  }
282  };
283 
284  template<>
285  struct unzip_if_impl<false>
286  {
287  template<typename Rng>
288  static Rng&& impl(Rng&& rng)
289  {
290  return std::forward<Rng>(rng);
291  }
292  };
293 
294 } // namespace detail
295 
316 template<bool Enable, typename RangeT>
317 decltype(auto) unzip_if(RangeT&& range)
318 {
319  return detail::unzip_if_impl<Enable>::impl(std::forward<RangeT>(range));
320 }
321 
322 // maybe untuple //
323 
324 namespace detail {
325 
326  template<std::size_t Size>
327  struct maybe_untuple_impl
328  {
329  template<typename Tuple>
330  static Tuple&& impl(Tuple&& tuple)
331  {
332  return std::forward<Tuple>(tuple);
333  }
334  };
335 
336  template<>
337  struct maybe_untuple_impl<1>
338  {
339  template<typename Tuple>
340  static decltype(auto) impl(Tuple&& tuple)
341  {
342  return std::get<0>(std::forward<Tuple>(tuple));
343  }
344  };
345 
346 } // namespace detail
347 
370 template<typename Tuple>
371 decltype(auto) maybe_untuple(Tuple&& tuple)
372 {
373  constexpr std::size_t tuple_size = std::tuple_size<std::decay_t<Tuple>>::value;
374  return detail::maybe_untuple_impl<tuple_size>::impl(std::forward<Tuple>(tuple));
375 }
376 
377 // times with index //
378 
379 namespace detail {
380 
381  template<typename Fun, std::size_t... Is>
382  constexpr Fun times_with_index_impl(Fun&& fun, std::index_sequence<Is...>)
383  {
384  (..., (std::invoke(fun, std::integral_constant<std::size_t, Is>{})));
385  return fun;
386  }
387 
388 } // namespace detail
389 
402 template<std::size_t N, typename Fun>
403 constexpr Fun times_with_index(Fun&& fun)
404 {
405  return detail::times_with_index_impl(std::forward<Fun>(fun), std::make_index_sequence<N>{});
406 }
407 
421 template <typename Tuple, typename Fun>
422 constexpr auto tuple_for_each_with_index(Tuple&& tuple, Fun&& fun)
423 {
424  return utility::times_with_index<std::tuple_size<std::decay_t<Tuple>>{}>(
425  [&fun, &tuple](auto index) {
426  std::invoke(fun, std::get<index>(tuple), index);
427  });
428 }
429 
430 // transform with index //
431 
432 namespace detail {
433 
434  template <typename Fun, typename Tuple, std::size_t... Is>
435  constexpr auto tuple_transform_with_index_impl(Tuple&& tuple, Fun&& fun,
436  std::index_sequence<Is...>)
437  {
438  return std::make_tuple(std::invoke(fun, std::get<Is>(std::forward<Tuple>(tuple)),
439  std::integral_constant<std::size_t, Is>{})...);
440  }
441 
442 } // namespace detail
443 
457 template <typename Tuple, typename Fun>
458 constexpr auto tuple_transform_with_index(Tuple&& tuple, Fun&& fun)
459 {
460  return detail::tuple_transform_with_index_impl(
461  std::forward<Tuple>(tuple), std::forward<Fun>(fun),
462  std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>{}>{});
463 }
464 
465 } // namespace hipipe::utility
466 
467 #endif
hipipe::utility::times_with_index
constexpr Fun times_with_index(Fun &&fun)
Repeat a function N times in compile time.
Definition: tuple.hpp:402
hipipe::utility::operator<<
std::ostream & operator<<(std::ostream &out, const std::tuple< Ts... > &tuple)
Tuple pretty printing to std::ostream.
Definition: tuple.hpp:185
hipipe::utility::tuple_print
std::ostream & tuple_print(std::ostream &out, const Tuple &tuple, std::index_sequence< Is... >)
Tuple pretty printing to std::ostream.
Definition: tuple.hpp:174
hipipe::utility::maybe_tuple_impl
Wrap variadic template pack in a tuple if there is more than one type.
Definition: tuple.hpp:55
hipipe::utility::plus
constexpr std::index_sequence<(Value+Is)... > plus(std::index_sequence< Is... >)
Add a number to all values in std::index_sequence.
Definition: tuple.hpp:76
hipipe::utility::variadic_find
Get the first index of a type in a variadic template list.
Definition: tuple.hpp:38
hipipe::utility::CPP_template
CPP_template(class Rng)(requires ranges
Unzips a range of tuples to a tuple of std::vectors.
Definition: tuple.hpp:255
hipipe::utility::tuple_transform_with_index
constexpr auto tuple_transform_with_index(Tuple &&tuple, Fun &&fun)
Similar to tuple_transform(), but with index available.
Definition: tuple.hpp:457
hipipe::utility::tuple_for_each_with_index
constexpr auto tuple_for_each_with_index(Tuple &&tuple, Fun &&fun)
Similar to tuple_for_each(), but with index available.
Definition: tuple.hpp:421
hipipe::utility::make_offset_index_sequence
decltype(plus< Offset >(std::make_index_sequence< N >{})) make_offset_index_sequence
Make std::index_sequence with the given offset.
Definition: tuple.hpp:90
hipipe::utility::tuple_transform
constexpr auto tuple_transform(Tuple &&tuple, Fun &&fun)
Transform each element of a tuple.
Definition: tuple.hpp:153
hipipe::utility::tuple_for_each
constexpr auto tuple_for_each(Tuple &&tuple, Fun &&fun)
Apply a function on each element of a tuple.
Definition: tuple.hpp:118
hipipe::utility::unzip_if
decltype(auto) unzip_if(RangeT &&range)
Unzips a range of tuples to a tuple of ranges if a constexpr condition holds.
Definition: tuple.hpp:316
hipipe::utility::maybe_untuple
decltype(auto) maybe_untuple(Tuple &&tuple)
Extract a value from a tuple if the tuple contains only a single value.
Definition: tuple.hpp:370
hipipe::utility::tuple_contains
Check whether a tuple contains a given type.
Definition: tuple.hpp:164