coveo::linq
Implementation of .NET-like LINQ operators in C++
sequence_util.h
Go to the documentation of this file.
1 /**
2  * @file
3  * @brief Utilities used by <tt>coveo::enumerable</tt>.
4  *
5  * @copyright 2016-2019, Coveo Solutions Inc.
6  * Distributed under the Apache License, Version 2.0 (see <a href="https://github.com/coveo/linq/blob/master/LICENSE">LICENSE</a>).
7  */
8 
9 #ifndef COVEO_SEQUENCE_UTIL_H
10 #define COVEO_SEQUENCE_UTIL_H
11 
12 #include <coveo/seq/detail/enumerable_detail.h>
13 
14 #include <cstddef>
15 #include <functional>
16 #include <iterator>
17 #include <type_traits>
18 #include <utility>
19 
20 namespace coveo {
21 
22 /// @cond
23 
24 // Forward declaration of enumerable, for type traits
25 template<typename> class enumerable;
26 
27 /// @endcond
28 
29 /**
30  * @brief Traits class for elements in a sequence.
31  * @headerfile sequence_util.h <coveo/seq/sequence_util.h>
32  *
33  * Traits class containing definitions pertaining to the elements of a sequence.
34  * For sequences of references or <tt>std::reference_wrapper</tt>s, provides
35  * information about the referred type instead.
36  *
37  * @tparam T Type of elements in the sequence.
38  */
39 template<typename T>
41 {
42  /**
43  * @brief Type of element in the sequence.
44  *
45  * Type of the sequence's elements. Corresponds to @c T.
46  */
47  using value_type = T;
48 
49  /**
50  * @brief Type of element in the sequence, but @c const.
51  *
52  * Same as <tt>coveo::seq_element_traits::value_type</tt>, but @c const.
53  */
54  using const_value_type = const value_type;
55 
56  /**
57  * @brief Raw type of element in the sequence.
58  *
59  * Same as <tt>coveo::seq_element_traits::value_type</tt>, but "raw", e.g. without @c const or @c volatile.
60  */
61  using raw_value_type = typename std::remove_cv<value_type>::type;
62 
63  /**
64  * @brief Pointer to a sequence element.
65  *
66  * Pointer to an element in the sequence.
67  * Corresponds to <tt>coveo::seq_element_traits::value_type*</tt>.
68  */
69  using pointer = value_type*;
70 
71  /**
72  * @brief Reference to a sequence element.
73  *
74  * Reference to an element in the sequence.
75  * Corresponds to <tt>coveo::seq_element_traits::value_type&</tt>.
76  */
77  using reference = value_type&;
78 
79  /**
80  * @brief Pointer to a @c const sequence element.
81  *
82  * Pointer to a @c const element in the sequence.
83  * Corresponds to <tt>coveo::seq_element_traits::const_value_type*</tt>.
84  */
85  using const_pointer = const_value_type*;
86 
87  /**
88  * @brief Reference to a @c const sequence element.
89  *
90  * Reference to a @c const element in the sequence.
91  * Corresponds to <tt>coveo::seq_element_traits::const_value_type&</tt>.
92  */
93  using const_reference = const_value_type&;
94 };
95 /// @cond
96 template<typename T> struct seq_element_traits<T&> : seq_element_traits<T> { };
97 template<typename T> struct seq_element_traits<T&&> : seq_element_traits<T> { };
98 template<typename T> struct seq_element_traits<std::reference_wrapper<T>> : seq_element_traits<T> { };
99 /// @endcond
100 
101 /**
102  * @brief Traits class for a sequence.
103  * @headerfile sequence_util.h <coveo/seq/sequence_util.h>
104  *
105  * Traits class containing definitions pertaining to a sequence. A shorthand
106  * for <tt>coveo::seq_element_traits</tt> that infers the sequence's @c value_type
107  * from the return value of its iterators. Also provides the type of iterator
108  * used by the sequence.
109  *
110  * @tparam Seq Type of sequence.
111  */
112 template<typename Seq>
113 struct seq_traits : public seq_element_traits<decltype(*std::begin(std::declval<Seq&>()))>
114 {
115  /**
116  * @brief Type of iterator used by the sequence.
117  *
118  * Type of iterator used by the sequence, which is defined as the type
119  * of iterator returned when calling <tt>std::begin()</tt>.
120  */
121  using iterator_type = typename std::decay<decltype(std::begin(std::declval<Seq&>()))>::type;
122 };
123 /// @cond
124 template<typename Seq> struct seq_traits<Seq&> : seq_traits<Seq> { };
125 template<typename Seq> struct seq_traits<Seq&&> : seq_traits<Seq> { };
126 template<typename Seq> struct seq_traits<std::reference_wrapper<Seq>> : seq_traits<Seq> { };
127 /// @endcond
128 
129 #ifdef DOXYGEN_INVOKED
130 /**
131  * @brief Traits class to identify enumerable objects.
132  * @headerfile sequence_util.h <coveo/seq/sequence_util.h>
133  *
134  * Traits class that can be used to identify enumerable objects.
135  *
136  * @tparam T Type to identify.
137  */
138 template<typename T> struct is_enumerable;
139 #else
140 template<typename> struct is_enumerable : std::false_type { };
141 template<typename T> struct is_enumerable<enumerable<T>> : std::true_type { };
142 #endif
143 
144 /**
145  * @brief Helper function to quickly reserve space in a container if possible.
146  * @headerfile sequence_util.h <coveo/seq/sequence_util.h>
147  *
148  * Attempts to @c reserve space in container @c cnt to hold as many
149  * elements as found in sequence @c seq. This is performed only if
150  * it's possible to do so "quickly".
151  *
152  * - If @c seq is a <tt>coveo::enumerable</tt>, space is reserved
153  * by using its <tt>coveo::enumerable::size()</tt> method if
154  * <tt>coveo::enumerable::has_fast_size()</tt> returns @c true.
155  * - If @c seq is a container with a <tt>size()</tt> method, space
156  * is reserved by using that method.
157  * - If @c seq is a sequence that uses random-access iterators,
158  * space is reserved by using <tt>std::distance()</tt>.
159  * - Otherwise, space is not reserved.
160  *
161  * @param cnt Container in which to reserve space.
162  * @param seq Sequence to use to try to reserve space for.
163  * @return @c true if space has actually been reserved in @c cnt.
164  */
165 template<typename C, typename Seq>
166 auto try_reserve(C& cnt, const Seq& seq) -> typename std::enable_if<is_enumerable<Seq>::value, bool>::type
167 {
168  const bool can_reserve = seq.has_fast_size();
169  if (can_reserve) {
170  cnt.reserve(seq.size());
171  }
172  return can_reserve;
173 }
174 template<typename C, typename Seq>
175 auto try_reserve(C& cnt, const Seq& seq) -> typename std::enable_if<!is_enumerable<Seq>::value &&
176  coveo::detail::has_size_const_method<Seq>::value, bool>::type
177 {
178  cnt.reserve(seq.size());
179  return true;
180 }
181 template<typename C, typename Seq>
182 auto try_reserve(C& cnt, const Seq& seq) -> typename std::enable_if<!coveo::detail::has_size_const_method<Seq>::value &&
183  std::is_base_of<std::random_access_iterator_tag,
184  typename std::iterator_traits<typename seq_traits<Seq>::iterator_type>::iterator_category>::value,
185  bool>::type
186 {
187  cnt.reserve(std::distance(std::begin(seq), std::end(seq)));
188  return true;
189 }
190 template<typename C, typename Seq>
191 auto try_reserve(C&, const Seq&) -> typename std::enable_if<!coveo::detail::has_size_const_method<typename std::decay<Seq>::type>::value &&
192  !std::is_base_of<std::random_access_iterator_tag,
193  typename std::iterator_traits<typename seq_traits<Seq>::iterator_type>::iterator_category>::value,
194  bool>::type
195 {
196  // Can't reserve, no fast way of doing so
197  return false;
198 }
199 
200 /**
201  * @brief Helper function to get a fast size delegate if possible.
202  * @headerfile sequence_util.h <coveo/seq/sequence_util.h>
203  *
204  * Attempts to create a <tt>coveo::enumerable::size_delegate</tt>
205  * that can quickly calculate the number of elements found in
206  * sequence @c seq. A size delegate is returned only if it's
207  * possible to calculate the number of elements "quickly".
208  *
209  * - If @c seq is a <tt>coveo::enumerable</tt>, its
210  * <tt>coveo::enumerable::size()</tt> method is used to produce
211  * the size delegate if <tt>coveo::enumerable::has_fast_size()</tt>
212  * returns @c true.
213  * - If @c seq is a container with a <tt>size()</tt> method, a size
214  * delegate is produced using that method.
215  * - If @c seq is a sequence that uses random-access iterators,
216  * a size delegate is produced by using <tt>std::distance()</tt>.
217  * - Otherwise, no size delegate is produced.
218  *
219  * @param seq Sequence to calculate the number of elements of
220  * in order to produce the size delegate.
221  * @return <tt>coveo::enumerable::size_delegate</tt> instance,
222  * or @c nullptr if it's not possible to quickly calculate
223  * the number of elements in @c seq.
224  * @see coveo::enumerable::size_delegate
225  */
226 template<typename Seq>
227 auto try_get_size_delegate(const Seq& seq) -> typename std::enable_if<is_enumerable<Seq>::value,
228  std::function<std::size_t()>>::type
229 {
230  std::function<std::size_t()> siz;
231  if (seq.has_fast_size()) {
232  const std::size_t size = seq.size();
233  siz = [size]() -> std::size_t { return size; };
234  }
235  return siz;
236 }
237 template<typename Seq>
238 auto try_get_size_delegate(const Seq& seq) -> typename std::enable_if<!is_enumerable<Seq>::value &&
239  coveo::detail::has_size_const_method<Seq>::value,
240  std::function<std::size_t()>>::type
241 {
242  const std::size_t size = seq.size();
243  return [size]() -> std::size_t { return size; };
244 }
245 template<typename Seq>
246 auto try_get_size_delegate(const Seq& seq) -> typename std::enable_if<!coveo::detail::has_size_const_method<Seq>::value &&
247  std::is_base_of<std::random_access_iterator_tag,
248  typename std::iterator_traits<typename seq_traits<Seq>::iterator_type>::iterator_category>::value,
249  std::function<std::size_t()>>::type
250 {
251  const std::size_t size = static_cast<std::size_t>(std::distance(std::begin(seq), std::end(seq)));
252  return [size]() -> std::size_t { return size; };
253 }
254 template<typename Seq>
255 auto try_get_size_delegate(const Seq&) -> typename std::enable_if<!coveo::detail::has_size_const_method<Seq>::value &&
256  !std::is_base_of<std::random_access_iterator_tag,
257  typename std::iterator_traits<typename seq_traits<Seq>::iterator_type>::iterator_category>::value,
258  std::function<std::size_t()>>::type
259 {
260  // No way to quickly determine size, don't try
261  return nullptr;
262 }
263 
264 /**
265  * @brief Helper function to get a fast size delegate from iterators if possible.
266  * @headerfile sequence_util.h <coveo/seq/sequence_util.h>
267  *
268  * Attempts to create a <tt>coveo::enumerable::size_delegate</tt> that can
269  * quickly calculate the number of elements in range <tt>[beg, end[</tt>.
270  * A size delegate is returned only if it's possible to calculate the number
271  * of elements "quickly".
272  *
273  * - If @c beg and @c end are random-access iterators,
274  * a size delegate is produced by using <tt>std::distance()</tt>.
275  * - Otherwise, no size delegate is produced.
276  *
277  * @param beg Iterator pointing at beginning of range.
278  * @param end Iterator pointing at end of range.
279  * @return <tt>coveo::enumerable::size_delegate</tt> instance,
280  * or @c nullptr if it's not possible to quickly calculate
281  * the number of elements in <tt>[beg, end[</tt>
282  * @see coveo::enumerable::size_delegate
283  */
284 template<typename It>
285 auto try_get_size_delegate_for_iterators(const It& beg, const It& end) -> typename std::enable_if<std::is_base_of<std::random_access_iterator_tag,
286  typename std::iterator_traits<It>::iterator_category>::value,
287  std::function<std::size_t()>>::type
288 {
289  return [beg, end]() -> std::size_t { return std::distance(beg, end); };
290 }
291 template<typename It>
292 auto try_get_size_delegate_for_iterators(const It&, const It&) -> typename std::enable_if<!std::is_base_of<std::random_access_iterator_tag,
293  typename std::iterator_traits<It>::iterator_category>::value,
294  std::function<std::size_t()>>::type
295 {
296  // No way to quickly determine size, don't try
297  return nullptr;
298 }
299 
300 } // coveo
301 
302 #endif // COVEO_SEQUENCE_UTIL_H
auto try_reserve(C &cnt, const Seq &seq) -> typename std::enable_if< is_enumerable< Seq >::value, bool >::type
Helper function to quickly reserve space in a container if possible.
Definition: sequence_util.h:166
auto try_get_size_delegate_for_iterators(const It &beg, const It &end) -> typename std::enable_if< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< It >::iterator_category >::value, std::function< std::size_t()>>::type
Helper function to get a fast size delegate from iterators if possible.
Definition: sequence_util.h:285
Traits class for a sequence.
Definition: sequence_util.h:113
Type-erased sequence wrapper.
Definition: enumerable.h:62
Traits class for elements in a sequence.
Definition: sequence_util.h:40
auto try_get_size_delegate(const Seq &seq) -> typename std::enable_if< is_enumerable< Seq >::value, std::function< std::size_t()>>::type
Helper function to get a fast size delegate if possible.
Definition: sequence_util.h:227