28 #ifndef VC_COMMON_SUBSCRIPT_H_
29 #define VC_COMMON_SUBSCRIPT_H_
31 #include <initializer_list>
32 #include <type_traits>
38 namespace Vc_VERSIONED_NAMESPACE
43 template <
typename Base>
class AdaptSubscriptOperator :
public Base
47 template <
typename... Args>
48 Vc_ALWAYS_INLINE AdaptSubscriptOperator(Args &&... arguments)
49 : Base(std::forward<Args>(arguments)...)
55 Vc_ALWAYS_INLINE AdaptSubscriptOperator(std::initializer_list<T> l)
61 using Base::operator[];
65 typename = enable_if<!std::is_arithmetic<
66 typename std::decay<I>::type>::value>
71 Vc_ALWAYS_INLINE
auto operator[](I &&arg_)
72 -> decltype(subscript_operator(*
this, std::forward<I>(arg_)))
74 return subscript_operator(*
this, std::forward<I>(arg_));
78 template <
typename I,
typename = enable_if<
79 !std::is_arithmetic<typename std::decay<I>::type>::value>>
80 Vc_ALWAYS_INLINE
auto operator[](I &&arg_)
const
81 -> decltype(subscript_operator(*
this, std::forward<I>(arg_)))
83 return subscript_operator(*
this, std::forward<I>(arg_));
89 template <class T, class = decltype(convertIndexVector(std::declval<T>()))>
90 std::true_type is_valid_indexvector(T &&);
91 std::false_type is_valid_indexvector(...);
93 template <
class IndexVector,
class Test = decltype(is_valid_indexvector(
94 std::declval<const IndexVector &>()))>
95 struct is_valid_indexvector_ :
public std::integral_constant<bool, Test::value> {
97 static_assert(!is_valid_indexvector_<const int *>::value,
98 "Pointer is incorrectly classified as valid index vector type");
99 static_assert(is_valid_indexvector_<
const int[4]>::value,
100 "C-Array is incorrectly classified as invalid index vector type");
104 template <
typename Scale,
typename T>
105 Vc_ALWAYS_INLINE enable_if<Scale::num == Scale::den, Traits::decay<T>> applyScale(T &&x)
107 return std::forward<T>(x);
110 template <
typename Scale,
typename T>
111 Vc_ALWAYS_INLINE enable_if<
112 Scale::num != Scale::den && Traits::has_multiply_operator<T, int>::value,
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 "
121 constexpr
int value = Scale::num / Scale::den;
122 Vc_ASSERT(
Vc::all_of((x * value) / value == x));
123 return std::forward<T>(x) * value;
126 template <
typename Scale,
typename T>
127 Vc_ALWAYS_INLINE enable_if<
128 Scale::num != Scale::den && !Traits::has_multiply_operator<T, int>::value,
132 static_assert(Scale::num % Scale::den == 0,
133 "Non-integral index scaling requested. This typically happens only for "
134 "Vc::Scalar on 32-bit for gathers on double. You can work around the "
135 "issue by ensuring that all doubles in the structure are aligned on 8 "
137 constexpr
int value = Scale::num / Scale::den;
138 for (
size_t i = 0; i < x.size(); ++i) {
139 Vc_ASSERT((x[i] * value) / value == x[i]);
145 template <
typename Scale,
typename T,
typename U,
146 typename = enable_if<Traits::has_multiply_operator<T, int>::value &&
147 Traits::has_addition_operator<T, U>::value>>
148 Vc_ALWAYS_INLINE
typename std::decay<T>::type applyScaleAndAdd(T &&x, U &&y)
150 constexpr
int value = Scale::num / Scale::den;
152 return std::forward<T>(x) + std::forward<U>(y);
154 return std::forward<T>(x) * value + std::forward<U>(y);
158 typename Scale,
typename T,
typename U,
159 typename = enable_if<
160 !(Traits::has_multiply_operator<T &, int>::value &&
161 Traits::has_addition_operator<T &, decltype(std::declval<U>()[0])>::value) &&
162 Traits::has_subscript_operator<U>::value>>
163 Vc_ALWAYS_INLINE T applyScaleAndAdd(T x, U &&y)
165 constexpr
int value = Scale::num / Scale::den;
166 for (
size_t i = 0; i < x.size(); ++i) {
170 x[i] = x[i] * value + y[i];
176 template <
typename Scale,
typename T,
typename U>
177 Vc_ALWAYS_INLINE enable_if<!(Traits::has_multiply_operator<T &, int>::value &&
178 Traits::has_addition_operator<T &, U>::value) &&
179 !Traits::has_subscript_operator<U>::value,
181 applyScaleAndAdd(T x, U &&y)
183 constexpr
int value = Scale::num / Scale::den;
184 for (
size_t i = 0; i < x.size(); ++i) {
188 x[i] = x[i] * value + y;
195 template <std::size_t MinSize,
197 bool = Traits::is_simd_vector<IndexT>::value>
198 struct IndexVectorSizeMatches
199 :
public std::true_type
206 template <std::
size_t MinSize,
typename V>
207 struct IndexVectorSizeMatches<MinSize,
209 true> :
public std::integral_constant<bool, (MinSize <= V::Size)>
213 template <std::size_t MinSize, typename T, std::size_t ArraySize>
214 struct IndexVectorSizeMatches<MinSize,
216 false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
220 template <std::size_t MinSize, typename T, std::size_t ArraySize>
221 struct IndexVectorSizeMatches<MinSize,
222 std::array<T, ArraySize>,
223 false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
227 template <std::size_t MinSize, typename T, std::size_t ArraySize>
228 struct IndexVectorSizeMatches<MinSize,
229 Vc::array<T, ArraySize>,
230 false> : public std::integral_constant<bool, (MinSize <= ArraySize)>
234 template <std::size_t MinSize, typename T, std::ptrdiff_t N>
235 struct IndexVectorSizeMatches<MinSize, Vc::Common::span<T, N>, false>
236 : public std::integral_constant<bool, (N == -1 || MinSize <= N)> {
240 typename T, typename IndexVector, typename Scale = std::ratio<1, 1>,
241 bool = is_valid_indexvector_<IndexVector>::value>
242 class SubscriptOperation
244 const IndexVector m_indexes;
246 using ScalarType = typename std::decay<T>::type;
248 using IndexVectorScaled = Traits::decay<decltype(convertIndexVector(std::declval<const IndexVector &>()))>;
252 SubscriptOperation &operator=(const SubscriptOperation &) = delete;
253 SubscriptOperation(const SubscriptOperation &) = delete;
254 #ifndef __cpp_guaranteed_copy_elision
255 constexpr SubscriptOperation(SubscriptOperation &&) = default;
258 template <typename U,
259 typename = enable_if<((std::is_convertible<const U &, IndexVector>::value ||
260 std::is_same<U, IndexVector>::value) &&
261 std::is_copy_constructible<IndexVector>::value)>>
262 constexpr Vc_ALWAYS_INLINE SubscriptOperation(T *address, const U &indexes)
263 : m_indexes(indexes), m_address(address)
267 template <std::size_t... Indexes>
268 constexpr Vc_ALWAYS_INLINE SubscriptOperation(T *address, const IndexVector &indexes,
269 index_sequence<Indexes...>)
270 : m_indexes{indexes[Indexes]...}, m_address(address)
273 template <typename U>
274 constexpr Vc_ALWAYS_INLINE SubscriptOperation(
275 T *address, const U &indexes,
276 enable_if<((std::is_convertible<const U &, IndexVector>::value ||
277 std::is_same<U, IndexVector>::value) &&
278 !std::is_copy_constructible<IndexVector>::value &&
279 std::is_array<IndexVector>::value &&
280 std::extent<IndexVector>::value > 0)> = nullarg)
281 : SubscriptOperation(address, indexes,
282 make_index_sequence<std::extent<IndexVector>::value>())
286 static constexpr bool need_explicit_scaling =
287 Scale::num % Scale::den != 0 || Scale::num / Scale::den * sizeof(T) > 8;
290 GatherArguments<typename std::remove_cv<T>::type, IndexVectorScaled,
291 (need_explicit_scaling ? 1 : Scale::num / Scale::den)>
294 static_assert(std::is_arithmetic<ScalarType>::value,
295 "Incorrect type for a SIMD vector gather. Must be an arithmetic type.");
296 return {applyScale<typename std::conditional<need_explicit_scaling, Scale,
297 std::ratio<1, 1>>::type>(
298 convertIndexVector(m_indexes)),
302 Vc_ALWAYS_INLINE ScatterArguments<T, IndexVectorScaled> scatterArguments() &&
304 static_assert(std::is_arithmetic<ScalarType>::value,
305 "Incorrect type for a SIMD vector scatter. Must be an arithmetic type.");
306 return {applyScale<Scale>(convertIndexVector(m_indexes)), m_address};
309 template <typename V,
310 typename = enable_if<(std::is_arithmetic<ScalarType>::value &&Traits::is_simd_vector<
311 V>::value &&IndexVectorSizeMatches<V::Size, IndexVector>::value)>>
312 Vc_INTRINSIC operator V() &&
314 return V(static_cast<SubscriptOperation &&>(*this).gatherArguments());
317 template <typename V,
318 typename = enable_if<(std::is_arithmetic<ScalarType>::value &&Traits::is_simd_vector<
319 V>::value &&IndexVectorSizeMatches<V::Size, IndexVector>::value)>>
320 Vc_ALWAYS_INLINE SubscriptOperation &operator=(const V &rhs) &&
322 static_assert(std::is_arithmetic<ScalarType>::value,
323 "Incorrect type for a SIMD vector scatter. Must be an arithmetic type.");
324 const auto indexes = applyScale<Scale>(convertIndexVector(m_indexes));
325 rhs.scatter(m_address, indexes);
335 typename = enable_if<std::is_same<S, typename std::remove_cv<T>::type>::value &&(
336 std::is_class<T>::value || std::is_union<T>::value)>>
337 Vc_ALWAYS_INLINE auto operator[](U S::*member) &&
338 -> SubscriptOperation<
339 typename std::conditional<std::is_const<T>::value,
340 const typename std::remove_reference<U>::type,
341 typename std::remove_reference<U>::type>::type,
348 std::ratio_multiply<Scale, std::ratio<sizeof(S), sizeof(U)>>>
350 static_assert(std::is_same<Traits::decay<decltype(m_address->*member)>,
351 Traits::decay<U>>::value,
352 "Type mismatch that should be impossible.");
354 return {&(m_address->*member), m_indexes};
379 template <intmax_t N, intmax_t D> struct make_ratio {
380 using type = std::ratio<N, D == 0 ? 1 : D>;
385 template <
typename U>
389 Vc_ALWAYS_INLINE
auto operator[](U index) && ->
typename std::enable_if<
390 #ifndef Vc_IMPROVE_ERROR_MESSAGES
391 Traits::has_no_allocated_data<T>::value &&
393 std::is_convertible<U, size_t>::value,
398 typename std::remove_reference<decltype(m_address[0][index])>::type,
402 typename make_ratio<
sizeof(T),
sizeof(m_address[0][index])>::type>>>::type
404 static_assert(Traits::has_subscript_operator<T>::value,
405 "The subscript operator was called on a type that does not implement it.\n");
406 static_assert(Traits::has_no_allocated_data<T>::value,
407 "Invalid container type in gather/scatter operation.\nYou may only use "
408 "nested containers that store the data inside the object (such as builtin "
409 "arrays or std::array) but not containers that store data in allocated "
410 "memory (such as std::vector).\nSince this feature cannot be queried "
411 "generically at compile time you need to spezialize the "
412 "Vc::Traits::has_no_allocated_data_impl<T> type-trait for custom types that "
413 "meet the requirements.\n");
414 static_assert(std::is_lvalue_reference<decltype(m_address[0][index])>::value,
415 "The container does not return an lvalue reference to the data at "
416 "the requested offset. This makes it impossible to execute a "
417 "gather operation.\n");
418 return {&(m_address[0][index]), m_indexes};
422 template <
typename IT>
423 Vc_ALWAYS_INLINE
typename std::enable_if<
424 #ifndef Vc_IMPROVE_ERROR_MESSAGES
425 Traits::has_no_allocated_data<T>::value &&
426 Traits::has_subscript_operator<T>::value &&
428 Traits::has_subscript_operator<IT>::value,
429 SubscriptOperation<
typename std::remove_reference<decltype(
430 m_address[0][std::declval<
439 operator[](
const IT &index) &&
441 static_assert(Traits::has_subscript_operator<T>::value,
442 "The subscript operator was called on a type that does not implement it.\n");
443 static_assert(Traits::has_no_allocated_data<T>::value,
444 "Invalid container type in gather/scatter operation.\nYou may only use "
445 "nested containers that store the data inside the object (such as builtin "
446 "arrays or std::array) but not containers that store data in allocated "
447 "memory (such as std::vector).\nSince this feature cannot be queried "
448 "generically at compile time you need to spezialize the "
449 "Vc::Traits::has_no_allocated_data_impl<T> type-trait for custom types that "
450 "meet the requirements.\n");
451 return {&(m_address[0][0]),
452 applyScaleAndAdd<std::ratio_multiply<
453 Scale, std::ratio<
sizeof(T),
sizeof(m_address[0][0])>>>(
454 convertIndexVector(m_indexes), index)};
459 template <
typename T,
typename IndexVector,
typename Scale>
460 class SubscriptOperation<T, IndexVector, Scale, false>;
465 typename IndexVector,
466 typename = enable_if<
467 Traits::has_subscript_operator<IndexVector>::value
469 &&Traits::has_contiguous_storage<Container>::value
473 &&std::is_lvalue_reference<decltype(*begin(std::declval<
474 Container>()))>::value
478 Vc_ALWAYS_INLINE SubscriptOperation<
479 typename std::remove_reference<decltype(*begin(std::declval<Container>()))>::
485 typename std::remove_const<
typename std::remove_reference<
486 IndexVector>::type>::type
489 > subscript_operator(Container &&c, IndexVector &&indexes)
491 Vc_ASSERT(std::addressof(*begin(c)) + 1 ==
492 std::addressof(*(begin(c) + 1)));
496 return {std::addressof(*begin(c)), std::forward<IndexVector>(indexes)};
508 template <
typename Container,
typename I>
509 Vc_ALWAYS_INLINE Vc::Common::SubscriptOperation<
510 typename std::remove_reference<decltype(std::declval<Container>()[0])>::type,
511 const std::initializer_list<I> &> subscript_operator(Container &&vec,
512 const std::initializer_list<I> &indexes)
514 return {&vec[0], indexes};
520 using Common::subscript_operator;
524 #endif // VC_COMMON_SUBSCRIPT_H_