Vc  1.3.2-dev
SIMD Vector Classes for C++
subscript.h
1 /* This file is part of the Vc library. {{{
2 Copyright © 2013-2015 Matthias Kretz <kretz@kde.org>
3 
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are met:
6  * Redistributions of source code must retain the above copyright
7  notice, this list of conditions and the following disclaimer.
8  * Redistributions in binary form must reproduce the above copyright
9  notice, this list of conditions and the following disclaimer in the
10  documentation and/or other materials provided with the distribution.
11  * Neither the names of contributing organizations nor the
12  names of its contributors may be used to endorse or promote products
13  derived from this software without specific prior written permission.
14 
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
19 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 }}}*/
27 
28 #ifndef VC_COMMON_SUBSCRIPT_H_
29 #define VC_COMMON_SUBSCRIPT_H_
30 
31 #include <initializer_list>
32 #include <ratio>
33 #include <type_traits>
34 #include <vector>
35 #include "types.h"
36 #include "macros.h"
37 #include <assert.h>
38 
39 namespace Vc_VERSIONED_NAMESPACE
40 {
41 namespace Common
42 {
43 // AdaptSubscriptOperator {{{1
44 template <typename Base> class AdaptSubscriptOperator : public Base
45 {
46 public:
47  // perfect forward all Base constructors
48  template <typename... Args>
49  Vc_ALWAYS_INLINE AdaptSubscriptOperator(Args &&... arguments)
50  : Base(std::forward<Args>(arguments)...)
51  {
52  }
53 
54  // perfect forward all Base constructors
55  template <typename T>
56  Vc_ALWAYS_INLINE AdaptSubscriptOperator(std::initializer_list<T> l)
57  : Base(l)
58  {
59  }
60 
61  // explicitly enable Base::operator[] because the following would hide it
62  using Base::operator[];
63 
65  template <typename I,
66  typename = enable_if<!std::is_arithmetic<
67  typename std::decay<I>::type>::value> // arithmetic types
68  // should always use
69  // Base::operator[] and
70  // never match this one
71  >
72  Vc_ALWAYS_INLINE auto operator[](I &&arg_)
73  -> decltype(subscript_operator(*this, std::forward<I>(arg_)))
74  {
75  return subscript_operator(*this, std::forward<I>(arg_));
76  }
77 
78  // const overload of the above
79  template <typename I, typename = enable_if<
80  !std::is_arithmetic<typename std::decay<I>::type>::value>>
81  Vc_ALWAYS_INLINE auto operator[](I &&arg_) const
82  -> decltype(subscript_operator(*this, std::forward<I>(arg_)))
83  {
84  return subscript_operator(*this, std::forward<I>(arg_));
85  }
86 };
87 // apply Scale (std::ratio) functions {{{1
88 template <typename Scale, typename T>
89 Vc_ALWAYS_INLINE enable_if<Scale::num == Scale::den, Traits::decay<T>> applyScale(T &&x)
90 {
91  return std::forward<T>(x);
92 }
93 
94 template <typename Scale, typename T>
95 Vc_ALWAYS_INLINE enable_if<
96  Scale::num != Scale::den && Traits::has_multiply_operator<T, std::intmax_t>::value,
97  Traits::decay<T>>
98  applyScale(T &&x)
99 {
100  static_assert(Scale::num % Scale::den == 0,
101  "Non-integral index scaling requested. This typically happens only for "
102  "Vc::Scalar on 32-bit for gathers on double. You can work around the "
103  "issue by ensuring that all doubles in the structure are aligned on 8 "
104  "Bytes.");
105  constexpr auto value = Scale::num / Scale::den;
106  Vc_ASSERT(Vc::all_of((x * value) / value == x));
107  return std::forward<T>(x) * value;
108 }
109 
110 template <typename Scale, typename T>
111 Vc_ALWAYS_INLINE enable_if<
112  Scale::num != Scale::den && !Traits::has_multiply_operator<T, std::intmax_t>::value,
113  T>
114  applyScale(T x)
115 {
116  static_assert(Scale::num % Scale::den == 0,
117  "Non-integral index scaling requested. This typically happens only for "
118  "Vc::Scalar on 32-bit for gathers on double. You can work around the "
119  "issue by ensuring that all doubles in the structure are aligned on 8 "
120  "Bytes.");
121  constexpr auto value = Scale::num / Scale::den;
122  for (size_t i = 0; i < x.size(); ++i) {
123  Vc_ASSERT((x[i] * value) / value == x[i]);
124  x[i] *= value;
125  }
126  return x;
127 }
128 
129 template <typename Scale, typename T, typename U,
130  typename = enable_if<Traits::has_multiply_operator<T, std::intmax_t>::value &&
131  Traits::has_addition_operator<T, U>::value>>
132 Vc_ALWAYS_INLINE typename std::decay<T>::type applyScaleAndAdd(T &&x, U &&y)
133 {
134  constexpr auto value = Scale::num / Scale::den;
135  if (value == 1) { // static evaluation
136  return std::forward<T>(x) + std::forward<U>(y);
137  }
138  return std::forward<T>(x) * value + std::forward<U>(y);
139 }
140 
141 template <
142  typename Scale, typename T, typename U,
143  typename = enable_if<
144  !(Traits::has_multiply_operator<T &, std::intmax_t>::value &&
145  Traits::has_addition_operator<T &, decltype(std::declval<U>()[0])>::value) &&
146  Traits::has_subscript_operator<U>::value>>
147 Vc_ALWAYS_INLINE T applyScaleAndAdd(T x, U &&y)
148 {
149  constexpr auto value = Scale::num / Scale::den;
150  for (size_t i = 0; i < x.size(); ++i) {
151  if (value == 1) { // static evaluation
152  x[i] = x[i] + y[i];
153  } else {
154  x[i] = x[i] * value + y[i];
155  }
156  }
157  return x;
158 }
159 
160 template <typename Scale, typename T, typename U>
161 Vc_ALWAYS_INLINE enable_if<!(Traits::has_multiply_operator<T &, std::intmax_t>::value &&
162  Traits::has_addition_operator<T &, U>::value) &&
163  !Traits::has_subscript_operator<U>::value,
164  T>
165  applyScaleAndAdd(T x, U &&y)
166 {
167  constexpr auto value = Scale::num / Scale::den;
168  for (size_t i = 0; i < x.size(); ++i) {
169  if (value == 1) { // static evaluation
170  x[i] = x[i] + y;
171  } else {
172  x[i] = x[i] * value + y;
173  }
174  }
175  return x;
176 }
177 
178 // IndexVectorSizeMatches {{{1
179 template <std::size_t MinSize,
180  typename IndexT,
181  bool = Traits::is_simd_vector<IndexT>::value>
182 struct IndexVectorSizeMatches
183  : public std::true_type // you might expect this should be false_type here, but the point is
184  // that IndexT is a type where the size is not known at compile time.
185  // Thus it may be good but we cannot know from the type. The only check
186  // we could do is a runtime check, but the type is fine.
187 {
188 };
189 
190 template <std::size_t MinSize, typename V>
191 struct IndexVectorSizeMatches<MinSize,
192  V,
193  true> : public std::integral_constant<bool, (MinSize <= V::Size)>
194 {
195 };
196 
197 template <std::size_t MinSize, typename T, std::size_t ArraySize>
198 struct IndexVectorSizeMatches<MinSize,
199  T[ArraySize],
200  false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
201 {
202 };
203 
204 template <std::size_t MinSize, typename T, std::size_t ArraySize>
205 struct IndexVectorSizeMatches<MinSize,
206  std::array<T, ArraySize>,
207  false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
208 {
209 };
210 
211 template <std::size_t MinSize, typename T, std::size_t ArraySize>
212 struct IndexVectorSizeMatches<MinSize,
213  Vc::array<T, ArraySize>,
214  false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
215 {
216 };
217 // convertIndexVector {{{1
218 // if the argument is a Vector<T> already we definitely want to keep it that way
219 template <typename IV>
220 enable_if<
221  (Traits::is_simd_vector<IV>::value && sizeof(typename IV::EntryType) >= sizeof(int)),
222  const IV &>
223  convertIndexVector(const IV &indexVector)
224 {
225  return indexVector;
226 }
227 
228 // but if the scalar (integral) type is smaller than int we convert it up to int. Otherwise it's
229 // very likely that the calculations we have to perform will overflow.
230 template <typename IV>
231 enable_if<
232  (Traits::is_simd_vector<IV>::value && sizeof(typename IV::EntryType) < sizeof(int)),
233  SimdArray<int, IV::Size>>
234  convertIndexVector(const IV &indexVector)
235 {
236  return static_cast<SimdArray<int, IV::Size>>(indexVector);
237 }
238 
239 // helper for promoting int types to int or higher
240 template<typename T> using promoted_type = decltype(std::declval<T>() + 1);
241 
242 // std::array, Vc::array, and C-array are fixed size and can therefore be converted to a
243 // SimdArray of the same size
244 template <typename T, std::size_t N>
245 enable_if<std::is_integral<T>::value, SimdArray<promoted_type<T>, N>> convertIndexVector(
246  const std::array<T, N> &indexVector)
247 {
248  return {std::addressof(indexVector[0]), Vc::Unaligned};
249 }
250 template <typename T, std::size_t N>
251 enable_if<std::is_integral<T>::value, SimdArray<promoted_type<T>, N>> convertIndexVector(
252  const Vc::array<T, N> &indexVector)
253 {
254  return {std::addressof(indexVector[0]), Vc::Unaligned};
255 }
256 template <typename T, std::size_t N>
257 enable_if<std::is_integral<T>::value, SimdArray<promoted_type<T>, N>> convertIndexVector(
258  const T (&indexVector)[N])
259 {
260  return SimdArray<promoted_type<T>, N>{std::addressof(indexVector[0]), Vc::Unaligned};
261 }
262 
263 // a plain pointer won't work. Because we need some information on the number of values in
264 // the index argument
265 #ifndef Vc_MSVC
266 // MSVC treats the function as usable in SFINAE context if it is deleted. If it's not declared we
267 // seem to get what we wanted (except for bad diagnostics)
268 template <typename T>
269 enable_if<std::is_pointer<T>::value, void> convertIndexVector(T indexVector) = delete;
270 #endif
271 
272 // an initializer_list works, but is runtime-sized (before C++14, at least) so we have to
273 // fall back to std::vector
274 template <typename T>
275 std::vector<promoted_type<T>> convertIndexVector(
276  const std::initializer_list<T> &indexVector)
277 {
278  return {begin(indexVector), end(indexVector)};
279 }
280 
281 // a std::vector cannot be converted to anything better
282 template <typename T>
283 enable_if<(std::is_integral<T>::value && sizeof(T) >= sizeof(int)), std::vector<T>>
284  convertIndexVector(const std::vector<T> &indexVector)
285 {
286  return indexVector;
287 }
288 template <typename T>
289 enable_if<(std::is_integral<T>::value && sizeof(T) < sizeof(int)),
290  std::vector<promoted_type<T>>>
291  convertIndexVector(const std::vector<T> &indexVector)
292 {
293  return {std::begin(indexVector), std::end(indexVector)};
294 }
295 
296 template <typename T>
297 std::true_type is_valid_indexvector(
298  T &&, Traits::decay<decltype(convertIndexVector(std::declval<T>()))> * = nullptr);
299 std::false_type is_valid_indexvector(...);
300 
301 template <typename IndexVector, typename Test = decltype(is_valid_indexvector(std::declval<const IndexVector &>()))>
302 struct is_valid_indexvector_ : public std::integral_constant<bool, Test::value>
303 {};
304 static_assert(!is_valid_indexvector_<const int *>::value, "Pointer is incorrectly classified as valid index vector type");
305 static_assert( is_valid_indexvector_<const int[4]>::value, "C-Array is incorrectly classified as invalid index vector type");
306 
307 // SubscriptOperation {{{1
308 template <
309  typename T, typename IndexVector, typename Scale = std::ratio<1, 1>,
310  bool = is_valid_indexvector_<IndexVector>::value>
311 class SubscriptOperation
312 {
313  const IndexVector m_indexes;
314  T *const m_address;
315  using ScalarType = typename std::decay<T>::type;
316 
317  using IndexVectorScaled = Traits::decay<decltype(convertIndexVector(std::declval<const IndexVector &>()))>;
318 
319 public:
320  template <typename U,
321  typename = enable_if<((std::is_convertible<const U &, IndexVector>::value ||
322  std::is_same<U, IndexVector>::value) &&
323  std::is_copy_constructible<IndexVector>::value)>>
324  constexpr Vc_ALWAYS_INLINE SubscriptOperation(T *address, const U &indexes)
325  : m_indexes(indexes), m_address(address)
326  {
327  }
328 
329  template <std::size_t... Indexes>
330  constexpr Vc_ALWAYS_INLINE SubscriptOperation(T *address, const IndexVector &indexes,
331  index_sequence<Indexes...>)
332  : m_indexes{indexes[Indexes]...}, m_address(address)
333  {}
334 
335  template <typename U>
336  constexpr Vc_ALWAYS_INLINE SubscriptOperation(
337  T *address, const U &indexes,
338  enable_if<((std::is_convertible<const U &, IndexVector>::value ||
339  std::is_same<U, IndexVector>::value) &&
340  !std::is_copy_constructible<IndexVector>::value &&
341  std::is_array<IndexVector>::value &&
342  std::extent<IndexVector>::value > 0)> = nullarg)
343  : SubscriptOperation(address, indexes,
344  make_index_sequence<std::extent<IndexVector>::value>())
345  {
346  }
347 
348  Vc_ALWAYS_INLINE GatherArguments<T, IndexVectorScaled> gatherArguments() const
349  {
350  static_assert(std::is_arithmetic<ScalarType>::value,
351  "Incorrect type for a SIMD vector gather. Must be an arithmetic type.");
352  return {applyScale<Scale>(convertIndexVector(m_indexes)), m_address};
353  }
354 
355  Vc_ALWAYS_INLINE ScatterArguments<T, IndexVectorScaled> scatterArguments() const
356  {
357  static_assert(std::is_arithmetic<ScalarType>::value,
358  "Incorrect type for a SIMD vector scatter. Must be an arithmetic type.");
359  return {applyScale<Scale>(convertIndexVector(m_indexes)), m_address};
360  }
361 
362  template <typename V,
363  typename = enable_if<(std::is_arithmetic<ScalarType>::value &&Traits::is_simd_vector<
364  V>::value &&IndexVectorSizeMatches<V::Size, IndexVector>::value)>>
365  Vc_ALWAYS_INLINE operator V() const
366  {
367  static_assert(std::is_arithmetic<ScalarType>::value,
368  "Incorrect type for a SIMD vector gather. Must be an arithmetic type.");
369  const auto indexes = applyScale<Scale>(convertIndexVector(m_indexes));
370  return V(m_address, indexes);
371  }
372 
373  template <typename V,
374  typename = enable_if<(std::is_arithmetic<ScalarType>::value &&Traits::is_simd_vector<
375  V>::value &&IndexVectorSizeMatches<V::Size, IndexVector>::value)>>
376  Vc_ALWAYS_INLINE SubscriptOperation &operator=(const V &rhs)
377  {
378  static_assert(std::is_arithmetic<ScalarType>::value,
379  "Incorrect type for a SIMD vector scatter. Must be an arithmetic type.");
380  const auto indexes = applyScale<Scale>(convertIndexVector(m_indexes));
381  rhs.scatter(m_address, indexes);
382  return *this;
383  }
384 
385  // precondition: m_address points to a struct/class/union
386  template <
387  typename U,
388  typename S, // S must be equal to T. Still we require this template parameter -
389  // otherwise instantiation of SubscriptOperation would only be valid for
390  // structs/unions.
391  typename = enable_if<std::is_same<S, typename std::remove_cv<T>::type>::value &&(
392  std::is_class<T>::value || std::is_union<T>::value)>>
393  Vc_ALWAYS_INLINE auto operator[](U S::*member)
394  -> SubscriptOperation<
395  typename std::conditional<std::is_const<T>::value,
396  const typename std::remove_reference<U>::type,
397  typename std::remove_reference<U>::type>::type,
398  IndexVector,
399  // By passing the scale factor as a fraction of integers in the template
400  // arguments the value does not lose information if the division yields a
401  // non-integral value. This could happen e.g. for a struct of struct (S2 {
402  // S1, char }, with sizeof(S1) = 16, sizeof(S2) = 20. Then scale would be
403  // 20/16)
404  std::ratio_multiply<Scale, std::ratio<sizeof(S), sizeof(U)>>>
405  {
406  static_assert(std::is_same<Traits::decay<decltype(m_address->*member)>,
407  Traits::decay<U>>::value,
408  "Type mismatch that should be impossible.");
409  // TODO: check whether scale really works for unions correctly
410  return {&(m_address->*member), m_indexes};
411  }
412 
413  /*
414  * The following functions allow subscripting of nested arrays. But
415  * there are two cases of containers and only one that we want to support:
416  * 1. actual arrays (e.g. T[N] or std::array<T, N>)
417  * 2. dynamically allocated vectors (e.g. std::vector<T>)
418  *
419  * For (1.) the offset calculation is straightforward.
420  * For (2.) the m_address pointer points to memory where pointers are
421  * stored to the actual data. Meaning the data can be scattered
422  * freely in memory (and far away from what m_address points to). Supporting this leads to
423  * serious trouble with the pointer (it does not really point to the start of a memory
424  * region anymore) and inefficient code. The user is better off to write a loop that assigns the
425  * scalars to the vector object sequentially.
426  */
427 
428 private:
429  // The following is a workaround for MSVC 2015 Update 2. Whenever the ratio
430  // in the return type of the following operator[] is encountered with a sizeof
431  // expression that fails, MSVC decides to substitute a 0 for the sizeof instead of
432  // just leaving the ratio instantiation alone via proper SFINAE. The make_ratio helper
433  // ensures that the 0 from the sizeof failure does not reach the denominator of
434  // std::ratio where it would hit a static_assert.
435  template <intmax_t N, intmax_t D> struct make_ratio {
436  using type = std::ratio<N, D == 0 ? 1 : D>;
437  };
438 
439 public:
440  // precondition: m_address points to a type that implements the subscript operator
441  template <typename U>
442  // U is only required to delay name lookup to the 2nd phase (on use).
443  // This is necessary because m_address[0][index] is only a correct
444  // expression if has_subscript_operator<T>::value is true.
445  Vc_ALWAYS_INLINE auto operator[](U index) -> typename std::enable_if<
446 #ifndef Vc_IMPROVE_ERROR_MESSAGES
447  Traits::has_no_allocated_data<T>::value &&
448 #endif
449  std::is_convertible<U, size_t>::value,
450  SubscriptOperation<
451  // the following decltype expression must depend on index and cannot
452  // simply use [0][0] because it would yield an invalid expression in
453  // case m_address[0] returns a struct/union
454  typename std::remove_reference<decltype(m_address[0][index])>::type,
455  IndexVector,
456  std::ratio_multiply<
457  Scale,
458  typename make_ratio<sizeof(T), sizeof(m_address[0][index])>::type>>>::type
459  {
460  static_assert(Traits::has_subscript_operator<T>::value,
461  "The subscript operator was called on a type that does not implement it.\n");
462  static_assert(Traits::has_no_allocated_data<T>::value,
463  "Invalid container type in gather/scatter operation.\nYou may only use "
464  "nested containers that store the data inside the object (such as builtin "
465  "arrays or std::array) but not containers that store data in allocated "
466  "memory (such as std::vector).\nSince this feature cannot be queried "
467  "generically at compile time you need to spezialize the "
468  "Vc::Traits::has_no_allocated_data_impl<T> type-trait for custom types that "
469  "meet the requirements.\n");
470  static_assert(std::is_lvalue_reference<decltype(m_address[0][index])>::value,
471  "The container does not return an lvalue reference to the data at "
472  "the requested offset. This makes it impossible to execute a "
473  "gather operation.\n");
474  return {&(m_address[0][index]), m_indexes};
475  }
476 
477  // precondition: m_address points to a type that implements the subscript operator
478  template <typename IT>
479  Vc_ALWAYS_INLINE typename std::enable_if<
480 #ifndef Vc_IMPROVE_ERROR_MESSAGES
481  Traits::has_no_allocated_data<T>::value &&
482  Traits::has_subscript_operator<T>::value &&
483 #endif
484  Traits::has_subscript_operator<IT>::value,
485  SubscriptOperation<typename std::remove_reference<decltype(
486  m_address[0][std::declval<
487  const IT &>()[0]] // std::declval<IT>()[0] could
488  // be replaced with 0 if it
489  // were not for two-phase lookup. We need to make the
490  // m_address[0][0] expression dependent on IT
491  )>::type,
492  IndexVectorScaled,
493  std::ratio<1, 1> // reset Scale to 1 since it is applied below
494  >>::type
495  operator[](const IT &index)
496  {
497  static_assert(Traits::has_subscript_operator<T>::value,
498  "The subscript operator was called on a type that does not implement it.\n");
499  static_assert(Traits::has_no_allocated_data<T>::value,
500  "Invalid container type in gather/scatter operation.\nYou may only use "
501  "nested containers that store the data inside the object (such as builtin "
502  "arrays or std::array) but not containers that store data in allocated "
503  "memory (such as std::vector).\nSince this feature cannot be queried "
504  "generically at compile time you need to spezialize the "
505  "Vc::Traits::has_no_allocated_data_impl<T> type-trait for custom types that "
506  "meet the requirements.\n");
507  return {&(m_address[0][0]),
508  applyScaleAndAdd<std::ratio_multiply<
509  Scale, std::ratio<sizeof(T), sizeof(m_address[0][0])>>>(
510  convertIndexVector(m_indexes), index)};
511  }
512 };
513 
514 // specialization for invalid IndexVector type
515 template <typename T, typename IndexVector, typename Scale>
516 class SubscriptOperation<T, IndexVector, Scale, false>;
517 
518 // subscript_operator {{{1
519 template <
520  typename Container,
521  typename IndexVector,
522  typename = enable_if<
523  Traits::has_subscript_operator<IndexVector>::value // The index vector must provide [] for
524  // the implementations of gather/scatter
525  &&Traits::has_contiguous_storage<Container>::value // Container must use contiguous
526  // storage, otherwise the index vector
527  // cannot be used as memory offsets, which is required for efficient
528  // gather/scatter implementations
529  &&std::is_lvalue_reference<decltype(*begin(std::declval<
530  Container>()))>::value // dereferencing the begin iterator must yield an lvalue
531  // reference (const or non-const). Otherwise it is not possible
532  // to determine a pointer to the data storage (see above).
533  >>
534 Vc_ALWAYS_INLINE SubscriptOperation<
535  typename std::remove_reference<decltype(*begin(std::declval<Container>()))>::
536  type, // the type of the first value in the container is what the internal array pointer
537  // has to point to. But if the subscript operator of the container returns a
538  // reference we need to drop that part because it's useless information for us. But
539  // const and volatile, as well as array rank/extent are interesting and need not be
540  // dropped.
541  typename std::remove_const<typename std::remove_reference<
542  IndexVector>::type>::type // keep volatile and possibly the array extent, but the const and
543  // & parts of the type need to be removed because
544  // SubscriptOperation explicitly adds them for its member type
545  > subscript_operator(Container &&c, IndexVector &&indexes)
546 {
547  Vc_ASSERT(std::addressof(*begin(c)) + 1 ==
548  std::addressof(*(begin(c) + 1))); // runtime assertion for contiguous storage, this
549  // requires a RandomAccessIterator - but that
550  // should be given for a container with contiguous
551  // storage
552  return {std::addressof(*begin(c)), std::forward<IndexVector>(indexes)};
553 }
554 
555 static_assert(!Traits::has_subscript_operator<SubscriptOperation<int[100][100], const int *>, int>::value, "has_subscript_operator fails");
556 static_assert(!Traits::has_subscript_operator<SubscriptOperation<int[100][100], const int *>, int[4]>::value, "has_subscript_operator fails");
557 static_assert(!Traits::has_subscript_operator<SubscriptOperation<std::vector<int>, const int *>, int[4]>::value, "has_subscript_operator fails");
558 static_assert( Traits::has_subscript_operator<SubscriptOperation<int[100][100], const int[4]>, int>::value, "has_subscript_operator fails");
559 static_assert( Traits::has_subscript_operator<SubscriptOperation<int[100][100], const int[4]>, int[4]>::value, "has_subscript_operator fails");
560 static_assert(!Traits::has_subscript_operator<SubscriptOperation<std::vector<int>, const int[4]>, int[4]>::value, "has_subscript_operator fails");
561 
571 template <typename Container, typename I>
572 Vc_ALWAYS_INLINE Vc::Common::SubscriptOperation<
573  typename std::remove_reference<decltype(std::declval<Container>()[0])>::type,
574  const std::initializer_list<I> &> subscript_operator(Container &&vec,
575  const std::initializer_list<I> &indexes)
576 {
577  return {&vec[0], indexes};
578 }
579 //}}}1
580 
581 } // namespace Common
582 
583 using Common::subscript_operator;
584 
585 namespace Traits
586 {
587 template <typename T, typename IndexVector, typename Scale>
588 struct is_subscript_operation_internal<Common::SubscriptOperation<T, IndexVector, Scale>> : public std::true_type
589 {
590 };
591 } // namespace Traits
592 
593 using Common::subscript_operator;
594 
595 } // namespace Vc
596 
597 #endif // VC_COMMON_SUBSCRIPT_H_
598 
599 // vim: foldmethod=marker
Definition: vector.h:257
constexpr bool all_of(const Mask &m)
Returns whether all entries in the mask m are true.
Definition: algorithms.h:44