HiPipe  0.6.0
C++17 data pipeline with Python bindings.
filter.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  ****************************************************************************/
10 
11 #pragma once
12 
13 #include <hipipe/core/stream/template_arguments.hpp>
14 #include <hipipe/core/stream/transform.hpp>
15 #include <hipipe/core/utility/tuple.hpp>
16 
17 #include <range/v3/view/filter.hpp>
18 #include <range/v3/view/move.hpp>
19 #include <range/v3/view/zip.hpp>
20 
21 #include <functional>
22 #include <utility>
23 
24 namespace hipipe::stream {
25 
26 namespace detail {
27 
28  // Filter the stream using the given function.
29  // This function wrapper is to be applied in one lower
30  // dimension than the wrapped function itself.
31  // This function wrapper is to be called in dimensions higher than 0.
32  template<typename Fun, typename From, typename ByIdxs>
33  struct wrap_filter_fun_for_transform;
34  template<typename Fun, typename... FromTypes, std::size_t... ByIdxs>
35  struct wrap_filter_fun_for_transform<Fun, from_t<FromTypes...>, std::index_sequence<ByIdxs...>>
36  {
37  Fun fun;
38 
39  // Properly zips/unzips the data and applies the filter function.
40  utility::maybe_tuple<FromTypes...> operator()(FromTypes&... cols)
41  {
42  // the following is much nicer when written as a pipeline, but this
43  // is more compilation time friendly
44  auto range_of_tuples =
45  ranges::view::move(
47  ranges::view::zip(cols...),
48  [this](const auto& tuple) -> bool {
49  return std::invoke(this->fun, std::get<ByIdxs>(tuple)...);
50  }
51  )
52  );
53 
54  return utility::maybe_untuple(utility::unzip(std::move(range_of_tuples)));
55  }
56  };
57 
58  // Helper function wrapper for dimension 0.
59  // This wrapper takes a single tuple of columns as argument and
60  // applies the stored function to a subset of columns selected by types.
61  // The columns are projected to their value().
62  template<typename Fun, typename... ByColumns>
63  struct apply_filter_fun_to_columns
64  {
65  Fun fun;
66 
67  bool operator()(const batch_t& source)
68  {
69  std::tuple<const typename ByColumns::data_type&...> slice_view{
70  source.extract<ByColumns>()...
71  };
72  static_assert(std::is_invocable_r_v<
73  bool, Fun&, const typename ByColumns::data_type&...>,
74  "hipipe::stream::filter: "
75  "The function has to accept the selected `by<>` columns (specifically "
76  "const ByColumns::data_type&) and return a bool.");
77  return std::apply(fun, std::move(slice_view));
78  }
79  };
80 
81  // Entry point for stream::filter.
82  // For dimensions higher than 0, use stream::transform to Dim-1 and
83  // wrap_filter_fun_for_transform wrapper.
84  template<int Dim>
85  struct filter_impl
86  {
87  template<typename... FromColumns, typename... ByColumns, typename Fun>
88  static auto impl(from_t<FromColumns...> f, by_t<ByColumns...> b, Fun fun)
89  {
90  static_assert(sizeof...(ByColumns) <= sizeof...(FromColumns),
91  "Cannot have more ByColumns than FromColumns.");
92  static_assert(
93  ((utility::ndims<typename FromColumns::data_type>::value >= Dim) && ...) &&
94  ((utility::ndims<typename ByColumns::data_type>::value >= Dim) && ...),
95  "hipipe::stream::filter: The dimension in which to apply the operation needs"
96  " to be at most the lowest dimension of all the from<> and by<> columns.");
97 
98  detail::wrap_filter_fun_for_transform<
99  Fun, from_t<utility::ndim_type_t<typename FromColumns::data_type, Dim-1>...>,
100  std::index_sequence<utility::variadic_find<ByColumns, FromColumns...>::value...>>
101  fun_wrapper{std::move(fun)};
102 
103  return stream::transform(f, to<FromColumns...>, std::move(fun_wrapper), dim<Dim-1>);
104  }
105  };
106 
107  // Special case for batch filtering (Dim == 0).
108  template<>
109  struct filter_impl<0>
110  {
111  template<typename From, typename... ByColumns, typename Fun>
112  static auto impl(From, by_t<ByColumns...>, Fun fun)
113  {
114  apply_filter_fun_to_columns<Fun, ByColumns...> fun_wrapper{std::move(fun)};
115  return ranges::view::filter(std::move(fun_wrapper));
116  }
117  };
118 
119 } // namespace detail
120 
140 template<typename... FromColumns, typename... ByColumns, typename Fun, int Dim = 1>
141 auto filter(from_t<FromColumns...> f,
142  by_t<ByColumns...> b,
143  Fun fun,
144  dim_t<Dim> d = dim_t<1>{})
145 {
146  static_assert(
149  "hipipe::stream::filter: The dimension in which to apply the operation "
150  " needs to be at most the lowest dimension of all the from<> and by<> columns.");
151  // a bit of function type erasure to speed up compilation
152  using FunT = std::function<
153  bool(const utility::ndim_type_t<typename ByColumns::data_type, Dim>&...)>;
154  return detail::filter_impl<Dim>::impl(f, b, FunT{std::move(fun)});
155 }
156 
157 } // namespace hipipe::stream
STL namespace.
Gets the number of dimensions of a multidimensional range.
Definition: ndim.hpp:47
auto transform(from_t< FromColumns... > f, to_t< ToColumns... > t, Fun fun, dim_t< Dim > d=dim_t< 1 >{})
Transform a subset of hipipe columns to a different subset of hipipe columns.
Definition: transform.hpp:187
Definition: ndim.hpp:144
auto filter(from_t< FromColumns... > f, by_t< ByColumns... > b, Fun fun, dim_t< Dim > d=dim_t< 1 >{})
Filter stream data.
Definition: filter.hpp:141
auto unzip(Rng range_of_tuples)
Unzips a range of tuples to a tuple of ranges.
Definition: tuple.hpp:257
decltype(auto) maybe_untuple(Tuple &&tuple)
Extract a value from a tuple if the tuple contains only a single value.
Definition: tuple.hpp:371