cxtream  0.5.1
C++17 data pipeline with Python bindings.
tuple.hpp
1 /****************************************************************************
2  * cxtream library
3  * Copyright (c) 2017, Cognexa Solutions s.r.o.
4  * Author(s) Filip Matzner
5  *
6  * This file is distributed under the MIT License.
7  * See the accompanying file LICENSE.txt for the complete license agreement.
8  ****************************************************************************/
10 
11 #ifndef CXTREAM_CORE_TUPLE_UTILS_HPP
12 #define CXTREAM_CORE_TUPLE_UTILS_HPP
13 
14 #include <boost/hana.hpp>
15 #include <boost/hana/ext/std/tuple.hpp>
16 #include <range/v3/core.hpp>
17 #include <range/v3/size.hpp>
18 #include <range/v3/to_container.hpp>
19 
20 #include <experimental/tuple>
21 #include <ostream>
22 #include <type_traits>
23 #include <vector>
24 
25 namespace cxtream::utility {
26 
39 template<typename T1, typename T2, typename... Ts>
40 struct variadic_find : std::integral_constant<std::size_t, variadic_find<T1, Ts...>{}+1> {
41 };
42 
43 template<typename T, typename... Ts>
44 struct variadic_find<T, T, Ts...> : std::integral_constant<std::size_t, 0> {
45 };
46 
56 template<std::size_t N, typename... Ts>
58  using type = std::tuple<Ts...>;
59 };
60 
61 template<typename T>
62 struct maybe_tuple_impl<1, T> {
63  using type = T;
64 };
65 
66 template<typename... Ts>
67 using maybe_tuple = typename maybe_tuple_impl<sizeof...(Ts), Ts...>::type;
68 
77 template<std::size_t Value, std::size_t... Is>
78 constexpr std::index_sequence<(Value + Is)...> plus(std::index_sequence<Is...>)
79 {
80  return {};
81 }
82 
91 template <std::size_t Offset, std::size_t N>
92 using make_offset_index_sequence = decltype(plus<Offset>(std::make_index_sequence<N>{}));
93 
94 // tuple_for_each //
95 
96 namespace detail {
97 
98  template<typename Fun, typename Tuple, std::size_t... Is>
99  constexpr Fun tuple_for_each_impl(Tuple&& tuple, Fun&& fun, std::index_sequence<Is...>)
100  {
101  (..., (std::invoke(fun, std::get<Is>(std::forward<Tuple>(tuple)))));
102  return fun;
103  }
104 
105 } // namespace detail
106 
119 template<typename Tuple, typename Fun>
120 constexpr auto tuple_for_each(Tuple&& tuple, Fun&& fun)
121 {
122  constexpr std::size_t tuple_size = std::tuple_size<std::decay_t<Tuple>>::value;
123  return detail::tuple_for_each_impl(std::forward<Tuple>(tuple),
124  std::forward<Fun>(fun),
125  std::make_index_sequence<tuple_size>{});
126 }
127 
128 // tuple_transform //
129 
130 namespace detail {
131 
132  template<typename Tuple, typename Fun, std::size_t... Is>
133  constexpr auto tuple_transform_impl(Tuple&& tuple, Fun&& fun, std::index_sequence<Is...>)
134  {
135  return std::make_tuple(std::invoke(fun, std::get<Is>(std::forward<Tuple>(tuple)))...);
136  }
137 
138 } // end namespace detail
139 
154 template<typename Tuple, typename Fun>
155 constexpr auto tuple_transform(Tuple&& tuple, Fun&& fun)
156 {
157  constexpr std::size_t tuple_size = std::tuple_size<std::decay_t<Tuple>>::value;
158  return detail::tuple_transform_impl(std::forward<Tuple>(tuple),
159  std::forward<Fun>(fun),
160  std::make_index_sequence<tuple_size>{});
161 }
162 
165 template<typename T, typename Tuple = void>
167 
168 template<typename T, typename... Types>
169 struct tuple_contains<T, std::tuple<Types...>>
170  : std::disjunction<std::is_same<std::decay_t<T>, std::decay_t<Types>>...> {
171 };
172 
185 template<typename... Types, typename Tuple>
186 constexpr auto tuple_type_view(Tuple& tuple)
187 {
188  return std::make_tuple(std::ref(std::get<Types>(tuple))...);
189 }
190 
204 template<std::size_t... Idxs, typename Tuple>
205 constexpr auto tuple_index_view(Tuple& tuple, std::index_sequence<Idxs...> = {})
206 {
207  return std::make_tuple(std::ref(std::get<Idxs>(tuple))...);
208 }
209 
210 // tuple cat unique //
211 
212 namespace detail {
213 
214  // This finds the indices of the first elements of each unique type in
215  // a tuple. E.g, for tuple<int, double, int, char, double>, the
216  // unique_indices class inherits from std::index_sequence<0, 1, 3>
217  //
218  // This implementation is sligthly faster than the previous one
219  // which was using boost::hana.
220  template<typename Tuple,
221  typename Known = std::tuple<>,
222  typename Is = std::index_sequence<>,
223  std::size_t I = 0,
224  bool In = tuple_contains<std::tuple_element_t<0, Tuple>, Known>::value>
225  struct unique_indices_impl;
226 
227  template<typename THead1, typename THead2, typename... TTail, typename... Types,
228  std::size_t... Is, std::size_t I>
229  struct unique_indices_impl<std::tuple<THead1, THead2, TTail...>, std::tuple<Types...>,
230  std::index_sequence<Is...>, I, true>
231  : unique_indices_impl<std::tuple<THead2, TTail...>, std::tuple<Types...>,
232  std::index_sequence<Is...>, I + 1>
233  { };
234 
235  template<typename THead1, typename THead2, typename... TTail, typename... Types,
236  std::size_t... Is, std::size_t I>
237  struct unique_indices_impl<std::tuple<THead1, THead2, TTail...>, std::tuple<Types...>,
238  std::index_sequence<Is...>, I, false>
239  : unique_indices_impl<std::tuple<THead2, TTail...>, std::tuple<THead1, Types...>,
240  std::index_sequence<Is..., I>, I + 1>
241  { };
242 
243  template<typename THead, typename Known, std::size_t... Is, std::size_t I>
244  struct unique_indices_impl<std::tuple<THead>, Known, std::index_sequence<Is...>, I, false>
245  : std::index_sequence<Is..., I>
246  { };
247 
248  template<typename THead, typename Known, std::size_t... Is, std::size_t I>
249  struct unique_indices_impl<std::tuple<THead>, Known, std::index_sequence<Is...>, I, true>
250  : std::index_sequence<Is...>
251  { };
252 
253  template<typename Tuple>
254  struct unique_indices
255  : unique_indices_impl<Tuple>
256  { };
257 
258  template<>
259  struct unique_indices<std::tuple<>>
260  : std::index_sequence<>
261  { };
262 
263  template<std::size_t... Idxs, typename Tuple>
264  constexpr auto make_tuple_by_idxs(Tuple&& tuple, std::index_sequence<Idxs...> = {})
265  {
266  return std::make_tuple(std::get<Idxs>(std::forward<Tuple>(tuple))...);
267  }
268 
269  template<typename Tuple>
270  constexpr auto make_unique_tuple(Tuple tuple)
271  {
272  return detail::make_tuple_by_idxs(std::move(tuple), unique_indices<Tuple>{});
273  }
274 
275 } // namespace detail
276 
290 template <typename... Tuples>
291 constexpr auto tuple_cat_unique(Tuples&&... tuples)
292 {
293  auto all = std::tuple_cat(std::forward<Tuples>(tuples)...);
294  return detail::make_unique_tuple(std::move(all));
295 }
296 
299 template<typename Tuple, size_t... Is>
300 std::ostream& tuple_print(std::ostream& out, const Tuple& tuple, std::index_sequence<Is...>)
301 {
302  out << "(";
303  (..., (out << (Is == 0 ? "" : ", ") << std::get<Is>(tuple)));
304  out << ")";
305  return out;
306 }
307 
310 template<typename... Ts>
311 std::ostream& operator<<(std::ostream& out, const std::tuple<Ts...>& tuple)
312 {
313  return utility::tuple_print(out, tuple, std::make_index_sequence<sizeof...(Ts)>{});
314 }
315 
316 // tuple_remove //
317 
333 template<typename... Rem, typename Tuple>
334 constexpr auto tuple_remove(Tuple tuple)
335 {
336  constexpr auto to_remove = boost::hana::make_set(boost::hana::type_c<std::decay_t<Rem>>...);
337  return boost::hana::remove_if(std::move(tuple), [&to_remove](const auto& a) {
338  return boost::hana::contains(to_remove, boost::hana::type_c<std::decay_t<decltype(a)>>);
339  });
340 }
341 
342 // unzip //
343 
344 namespace detail {
345 
346  // wrap each type of a tuple in std::vector, i.e., make a tuple of empty vectors
347  template<typename Tuple, std::size_t... Is>
348  auto vectorize_tuple(std::index_sequence<Is...>)
349  {
350  return std::make_tuple(std::vector<std::tuple_element_t<Is, std::decay_t<Tuple>>>()...);
351  }
352 
353  // push elements from the given tuple to the corresponding vectors in a tuple of vectors
354  template<typename ToR, typename Tuple, std::size_t... Is>
355  void push_unzipped(ToR& tuple_of_ranges, Tuple&& tuple, std::index_sequence<Is...>)
356  {
357  (..., (std::get<Is>(tuple_of_ranges).push_back(std::get<Is>(std::forward<Tuple>(tuple)))));
358  }
359 
360  // if the size of the given range is known, return it, otherwise return 0
361  template<typename Rng, CONCEPT_REQUIRES_(ranges::SizedRange<Rng>())>
362  std::size_t safe_reserve_size(Rng&& rng)
363  {
364  return ranges::size(rng);
365  }
366  template<typename Rng, CONCEPT_REQUIRES_(!ranges::SizedRange<Rng>())>
367  std::size_t safe_reserve_size(Rng&& rng)
368  {
369  return 0;
370  }
371 
372  template<typename Rng>
373  auto unzip_impl(Rng& range_of_tuples)
374  {
375  using tuple_type = ranges::range_value_type_t<Rng>;
376  constexpr auto tuple_size = std::tuple_size<tuple_type>{};
377  constexpr auto indices = std::make_index_sequence<tuple_size>{};
378  std::size_t reserve_size = detail::safe_reserve_size(range_of_tuples);
379 
380  auto tuple_of_ranges = detail::vectorize_tuple<tuple_type>(indices);
382  tuple_of_ranges, [reserve_size](auto& rng) { rng.reserve(reserve_size); });
383 
384  for (auto& v : range_of_tuples) {
385  detail::push_unzipped(tuple_of_ranges, std::move(v), indices);
386  }
387 
388  return tuple_of_ranges;
389  }
390 
391 } // namespace detail
392 
407 template<typename Rng, CONCEPT_REQUIRES_(ranges::Range<Rng>() && !ranges::View<Rng>())>
408 auto unzip(Rng range_of_tuples)
409 {
410  // copy the given container and move elements out of it
411  return detail::unzip_impl(range_of_tuples);
412 }
413 
415 template<typename Rng, CONCEPT_REQUIRES_(ranges::View<Rng>())>
416 auto unzip(Rng view_of_tuples)
417 {
418  return utility::unzip(view_of_tuples | ranges::to_vector);
419 }
420 
421 // maybe unzip //
422 
423 namespace detail {
424 
425  template<bool Enable>
426  struct unzip_if_impl
427  {
428  template<typename Rng>
429  static decltype(auto) impl(Rng&& rng)
430  {
431  return utility::unzip(std::forward<Rng>(rng));
432  }
433  };
434 
435  template<>
436  struct unzip_if_impl<false>
437  {
438  template<typename Rng>
439  static constexpr Rng&& impl(Rng&& rng)
440  {
441  return std::forward<Rng>(rng);
442  }
443  };
444 
445 } // namespace detail
446 
467 template<bool Enable, typename RangeT>
468 decltype(auto) unzip_if(RangeT&& range)
469 {
470  return detail::unzip_if_impl<Enable>::impl(std::forward<RangeT>(range));
471 }
472 
473 // maybe untuple //
474 
475 namespace detail {
476 
477  template<std::size_t Size>
478  struct maybe_untuple_impl
479  {
480  template<typename Tuple>
481  static Tuple&& impl(Tuple&& tuple)
482  {
483  return std::forward<Tuple>(tuple);
484  }
485  };
486 
487  template<>
488  struct maybe_untuple_impl<1>
489  {
490  template<typename Tuple>
491  static decltype(auto) impl(Tuple&& tuple)
492  {
493  return std::get<0>(std::forward<Tuple>(tuple));
494  }
495  };
496 
497 } // namespace detail
498 
521 template<typename Tuple>
522 decltype(auto) maybe_untuple(Tuple&& tuple)
523 {
524  constexpr std::size_t tuple_size = std::tuple_size<std::decay_t<Tuple>>::value;
525  return detail::maybe_untuple_impl<tuple_size>::impl(std::forward<Tuple>(tuple));
526 }
527 
528 // range to tuple //
529 
530 namespace detail {
531 
532  template<typename Rng, std::size_t... Is>
533  constexpr auto range_to_tuple_impl(Rng rng, std::index_sequence<Is...>)
534  {
535  return std::make_tuple(std::move(ranges::at(std::forward<Rng>(rng), Is))...);
536  }
537 
538 } // namespace detail
539 
553 template<std::size_t N, typename RARng>
554 constexpr auto range_to_tuple(RARng&& rng)
555 {
556  assert(ranges::size(rng) >= N);
557  return detail::range_to_tuple_impl(std::forward<RARng>(rng), std::make_index_sequence<N>{});
558 }
559 
560 // times with index //
561 
562 namespace detail {
563 
564  template<typename Fun, std::size_t... Is>
565  constexpr Fun times_with_index_impl(Fun&& fun, std::index_sequence<Is...>)
566  {
567  (..., (std::invoke(fun, std::integral_constant<std::size_t, Is>{})));
568  return fun;
569  }
570 
571 } // namespace detail
572 
585 template<std::size_t N, typename Fun>
586 constexpr Fun times_with_index(Fun&& fun)
587 {
588  return detail::times_with_index_impl(std::forward<Fun>(fun), std::make_index_sequence<N>{});
589 }
590 
604 template <typename Tuple, typename Fun>
605 constexpr auto tuple_for_each_with_index(Tuple&& tuple, Fun&& fun)
606 {
607  return utility::times_with_index<std::tuple_size<std::decay_t<Tuple>>{}>(
608  [&fun, &tuple](auto index) {
609  std::invoke(fun, std::get<index>(tuple), index);
610  });
611 }
612 
613 // transform with index //
614 
615 namespace detail {
616 
617  template <typename Fun, typename Tuple, std::size_t... Is>
618  constexpr auto tuple_transform_with_index_impl(Tuple&& tuple, Fun&& fun,
619  std::index_sequence<Is...>)
620  {
621  return std::make_tuple(std::invoke(fun, std::get<Is>(std::forward<Tuple>(tuple)),
622  std::integral_constant<std::size_t, Is>{})...);
623  }
624 
625 } // namespace detail
626 
640 template <typename Tuple, typename Fun>
641 constexpr auto tuple_transform_with_index(Tuple&& tuple, Fun&& fun)
642 {
643  return detail::tuple_transform_with_index_impl(
644  std::forward<Tuple>(tuple), std::forward<Fun>(fun),
645  std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>{}>{});
646 }
647 
648 } // namespace cxtream::utility
649 
650 #endif
constexpr auto tuple_transform_with_index(Tuple &&tuple, Fun &&fun)
Similar to tuple_transform(), but with index available.
Definition: tuple.hpp:641
constexpr std::index_sequence<(Value+Is)... > plus(std::index_sequence< Is... >)
Add a number to all values in std::index_sequence.
Definition: tuple.hpp:78
constexpr auto tuple_cat_unique(Tuples &&... tuples)
Concatenate two tuples and keep only the first element of each type.
Definition: tuple.hpp:291
constexpr Fun times_with_index(Fun &&fun)
Repeat a function N times in compile time.
Definition: tuple.hpp:586
decltype(plus< Offset >(std::make_index_sequence< N >{})) make_offset_index_sequence
Make std::index_sequence with the given offset.
Definition: tuple.hpp:92
auto unzip(Rng range_of_tuples)
Unzips a range of tuples to a tuple of ranges.
Definition: tuple.hpp:408
STL namespace.
decltype(auto) unzip_if(RangeT &&range)
Unzips a range of tuples to a tuple of ranges if a constexpr condition holds.
Definition: tuple.hpp:468
decltype(auto) maybe_untuple(Tuple &&tuple)
Extract a value from a tuple if the tuple contains only a single value.
Definition: tuple.hpp:522
constexpr auto tuple_for_each(Tuple &&tuple, Fun &&fun)
Apply a function on each element of a tuple.
Definition: tuple.hpp:120
constexpr auto tuple_type_view(Tuple &tuple)
Makes a sub-tuple made of references to the original tuple (selected by type).
Definition: tuple.hpp:186
std::ostream & operator<<(std::ostream &out, const std::tuple< Ts... > &tuple)
Tuple pretty printing to std::ostream.
Definition: tuple.hpp:311
constexpr auto tuple_for_each_with_index(Tuple &&tuple, Fun &&fun)
Similar to tuple_for_each(), but with index available.
Definition: tuple.hpp:605
std::ostream & tuple_print(std::ostream &out, const Tuple &tuple, std::index_sequence< Is... >)
Tuple pretty printing to std::ostream.
Definition: tuple.hpp:300
constexpr auto tuple_remove(Tuple tuple)
Remove types from a tuple.
Definition: tuple.hpp:334
Check whether a tuple contains a given type.
Definition: tuple.hpp:166
Get the first index of a type in a variadic template list.
Definition: tuple.hpp:40
constexpr auto tuple_index_view(Tuple &tuple, std::index_sequence< Idxs... >={})
Makes a sub-tuple made of references to the original tuple (selected by index).
Definition: tuple.hpp:205
constexpr auto range_to_tuple(RARng &&rng)
Converts a range to a tuple.
Definition: tuple.hpp:554
Wrap variadic template pack in a tuple if there is more than one type.
Definition: tuple.hpp:57
constexpr auto tuple_transform(Tuple &&tuple, Fun &&fun)
Transform each element of a tuple.
Definition: tuple.hpp:155