1 /** @addtogroup expressions
2 * @{
3 */
4 /*
5 Copyright (C) 2016 D Levin (https://www.kfrlib.com)
6 This file is part of KFR
7
8 KFR is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
12
13 KFR is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with KFR.
20
21 If GPL is not suitable for your project, you must purchase a commercial license to use KFR.
22 Buying a commercial license is mandatory as soon as you develop commercial activities without
23 disclosing the source code of your own applications.
24 See https://www.kfrlib.com for details.
25 */
26 #pragma once
27
28 #include "../cometa/array.hpp"
29
30 #include "../simd/impl/function.hpp"
31 #include "../simd/read_write.hpp"
32 #include "../simd/types.hpp"
33 #include "memory.hpp"
34
35 CMT_PRAGMA_MSVC(warning(push))
36 CMT_PRAGMA_MSVC(warning(disable : 4324))
37
38 namespace kfr
39 {
40
41 using univector_tag = size_t;
42
43 enum : size_t
44 {
45 tag_array_ref = 0,
46 tag_dynamic_vector = max_size_t,
47 };
48
49 template <typename T, univector_tag Tag = tag_dynamic_vector>
50 struct abstract_vector;
51
52 template <typename T, univector_tag Size>
53 struct abstract_vector : std::array<T, Size>
54 {
55 using std::array<T, Size>::array;
56 };
57
58 template <typename T>
59 struct abstract_vector<T, tag_dynamic_vector> : std::vector<T, allocator<T>>
60 {
61 using std::vector<T, allocator<T>>::vector;
62 };
63
64 template <typename T>
65 struct abstract_vector<T, tag_array_ref> : array_ref<T>
66 {
67 using array_ref<T>::array_ref;
68 };
69
70 /**
71 * @brief Class that represent data in KFR. Many KFR functions can take this class as an argument.
72 * Can inherit from std::vector, std::array or keep only reference to data and its size.
73 *
74 * univector<float> is inherited from std::vector<float>
75 * univector<float, 10> is inherited from std::array<float, 10>
76 * univector<float, 0> contains only reference to data
77 *
78 * To convert a plain pointer to univector, call make_univector:
79 * @code
80 * double* buffer;
81 * size_t size;
82 * univector<double, 0> v = make_univector(buffer, size);
83 * // or pass result vector directly to a function:
84 * some_function(make_univector(buffer, size));
85 * @endcode
86 */
87 template <typename T, univector_tag Tag = tag_dynamic_vector>
88 struct univector;
89
90 /// @brief Base class for all univector specializations.
91 template <typename T, typename Class, bool is_expression>
92 struct univector_base;
93
94 template <typename T, typename Class>
95 struct univector_base<T, Class, true> : input_expression, output_expression
96 {
97 using input_expression::begin_block;
98 using input_expression::end_block;
99 using output_expression::begin_block;
100 using output_expression::end_block;
101
102 template <typename U, size_t N>
operator ()kfr::univector_base103 KFR_MEM_INTRINSIC void operator()(coutput_t, size_t index, const vec<U, N>& value)
104 {
105 T* data = derived_cast<Class>(this)->data();
106 write(ptr_cast<T>(data) + index, vec<T, N>(value));
107 }
108
109 template <typename Input, KFR_ENABLE_IF(is_input_expression<Input>)>
operator =kfr::univector_base110 KFR_MEM_INTRINSIC Class& operator=(Input&& input)
111 {
112 assign_expr(std::forward<Input>(input));
113 return *derived_cast<Class>(this);
114 }
115
116 #define KFR_UVEC_ASGN_OP(aop, op) \
117 template <typename Input> \
118 KFR_MEM_INTRINSIC Class& aop(Input&& input) \
119 { \
120 assign_expr(*derived_cast<Class>(this) op std::forward<Input>(input)); \
121 return *derived_cast<Class>(this); \
122 }
123 KFR_UVEC_ASGN_OP(operator+=, +)
124 KFR_UVEC_ASGN_OP(operator-=, -)
125 KFR_UVEC_ASGN_OP(operator*=, *)
126 KFR_UVEC_ASGN_OP(operator/=, /)
127 KFR_UVEC_ASGN_OP(operator%=, %)
128
129 KFR_UVEC_ASGN_OP(operator&=, &)
130 KFR_UVEC_ASGN_OP(operator|=, |)
131 KFR_UVEC_ASGN_OP(operator^=, ^)
132
133 KFR_UVEC_ASGN_OP(operator<<=, <<)
134 KFR_UVEC_ASGN_OP(operator>>=, >>)
135
136 /// @brief Returns subrange of the vector.
137 /// If start is greater or equal to this->size, returns empty univector
138 /// If requested size is greater than this->size, returns only available elements
slicekfr::univector_base139 univector<T, 0> slice(size_t start = 0, size_t size = max_size_t)
140 {
141 T* data = derived_cast<Class>(this)->data();
142 const size_t this_size = derived_cast<Class>(this)->size();
143 return array_ref<T>(data + start, std::min(size, start < this_size ? this_size - start : 0));
144 }
145
146 /// @brief Returns subrange of the vector.
147 /// If start is greater or equal to this->size, returns empty univector
148 /// If requested size is greater than this->size, returns only available elements
slicekfr::univector_base149 univector<const T, 0> slice(size_t start = 0, size_t size = max_size_t) const
150 {
151 const T* data = derived_cast<Class>(this)->data();
152 const size_t this_size = derived_cast<Class>(this)->size();
153 return array_ref<const T>(data + start, std::min(size, start < this_size ? this_size - start : 0));
154 }
155
156 /// @brief Returns subrange of the vector starting from 0.
157 /// If requested size is greater than this->size, returns only available elements
truncatekfr::univector_base158 univector<T, 0> truncate(size_t size = max_size_t)
159 {
160 T* data = derived_cast<Class>(this)->data();
161 const size_t this_size = derived_cast<Class>(this)->size();
162 return array_ref<T>(data, std::min(size, this_size));
163 }
164
165 /// @brief Returns subrange of the vector starting from 0.
166 /// If requested size is greater than this->size, returns only available elements
truncatekfr::univector_base167 univector<const T, 0> truncate(size_t size = max_size_t) const
168 {
169 const T* data = derived_cast<Class>(this)->data();
170 const size_t this_size = derived_cast<Class>(this)->size();
171 return array_ref<const T>(data, std::min(size, this_size));
172 }
173
refkfr::univector_base174 array_ref<T> ref()
175 {
176 T* data = get_data();
177 const size_t size = get_size();
178 return array_ref<T>(data, size);
179 }
refkfr::univector_base180 array_ref<const T> ref() const
181 {
182 const T* data = get_data();
183 const size_t size = get_size();
184 return array_ref<const T>(data, size);
185 }
crefkfr::univector_base186 array_ref<const T> cref() const
187 {
188 const T* data = get_data();
189 const size_t size = get_size();
190 return array_ref<const T>(data, size);
191 }
192
ringbuf_writekfr::univector_base193 void ringbuf_write(size_t& cursor, const T* src, size_t srcsize)
194 {
195 if (srcsize == 0)
196 return;
197 // skip redundant data
198 const size_t size = get_size();
199 T* data = get_data();
200 if (srcsize > size)
201 {
202 src = src + srcsize / size;
203 srcsize = srcsize % size;
204 }
205 const size_t fsize = size - cursor;
206 // one fragment
207 if (srcsize <= fsize)
208 {
209 copy(data + cursor, src, srcsize);
210 }
211 else // two fragments
212 {
213 copy(data + cursor, src, fsize);
214 copy(data, src + fsize, srcsize - fsize);
215 }
216 ringbuf_step(cursor, srcsize);
217 }
218 template <size_t N>
ringbuf_writekfr::univector_base219 void ringbuf_write(size_t& cursor, const vec<T, N>& x)
220 {
221 ringbuf_write(cursor, ptr_cast<T>(&x), N);
222 }
ringbuf_writekfr::univector_base223 void ringbuf_write(size_t& cursor, const T& value)
224 {
225 T* data = get_data();
226 data[cursor] = value;
227 ringbuf_step(cursor, 1);
228 }
ringbuf_stepkfr::univector_base229 void ringbuf_step(size_t& cursor, size_t step) const
230 {
231 const size_t size = get_size();
232 cursor = cursor + step;
233 cursor = cursor >= size ? cursor - size : cursor;
234 }
ringbuf_readkfr::univector_base235 void ringbuf_read(size_t& cursor, T& value)
236 {
237 T* data = get_data();
238 value = data[cursor];
239 ringbuf_step(cursor, 1);
240 }
241 template <size_t N>
ringbuf_readkfr::univector_base242 void ringbuf_read(size_t& cursor, vec<T, N>& x)
243 {
244 ringbuf_read(cursor, ptr_cast<T>(&x), N);
245 }
ringbuf_readkfr::univector_base246 void ringbuf_read(size_t& cursor, T* dest, size_t destsize) const
247 {
248 if (destsize == 0)
249 return;
250 // skip redundant data
251 const size_t size = get_size();
252 const T* data = get_data();
253 if (destsize > size)
254 {
255 dest = dest + destsize / size;
256 destsize = destsize % size;
257 }
258 const size_t fsize = size - cursor;
259 // one fragment
260 if (destsize <= fsize)
261 {
262 copy(dest, data + cursor, destsize);
263 }
264 else // two fragments
265 {
266 copy(dest, data + cursor, fsize);
267 copy(dest + fsize, data, destsize - fsize);
268 }
269 ringbuf_step(cursor, destsize);
270 }
271
272 protected:
273 template <typename Input>
assign_exprkfr::univector_base274 KFR_MEM_INTRINSIC void assign_expr(Input&& input)
275 {
276 process(*derived_cast<Class>(this), std::forward<Input>(input));
277 }
278
279 private:
get_sizekfr::univector_base280 KFR_MEM_INTRINSIC size_t get_size() const { return derived_cast<Class>(this)->size(); }
get_datakfr::univector_base281 KFR_MEM_INTRINSIC const T* get_data() const { return derived_cast<Class>(this)->data(); }
get_datakfr::univector_base282 KFR_MEM_INTRINSIC T* get_data() { return derived_cast<Class>(this)->data(); }
283
copykfr::univector_base284 static void copy(T* dest, const T* src, size_t size)
285 {
286 for (size_t i = 0; i < size; ++i)
287 *dest++ = *src++;
288 }
289 };
290
291 template <typename T, typename Class>
292 struct univector_base<T, Class, false>
293 {
refkfr::univector_base294 array_ref<T> ref()
295 {
296 T* data = get_data();
297 const size_t size = get_size();
298 return array_ref<T>(data, size);
299 }
refkfr::univector_base300 array_ref<const T> ref() const
301 {
302 const T* data = get_data();
303 const size_t size = get_size();
304 return array_ref<const T>(data, size);
305 }
crefkfr::univector_base306 array_ref<const T> cref() const
307 {
308 const T* data = get_data();
309 const size_t size = get_size();
310 return array_ref<const T>(data, size);
311 }
312
313 template <typename Input, KFR_ENABLE_IF(is_input_expression<Input>)>
operator =kfr::univector_base314 KFR_MEM_INTRINSIC Class& operator=(Input&& input)
315 {
316 static_assert(sizeof(Input) == 0, "Can't assign expression to non-expression");
317 return *derived_cast<Class>(this);
318 }
319
320 private:
get_sizekfr::univector_base321 KFR_MEM_INTRINSIC size_t get_size() const { return derived_cast<Class>(this)->size(); }
get_datakfr::univector_base322 KFR_MEM_INTRINSIC const T* get_data() const { return derived_cast<Class>(this)->data(); }
get_datakfr::univector_base323 KFR_MEM_INTRINSIC T* get_data() { return derived_cast<Class>(this)->data(); }
324 };
325
326 template <typename T, size_t Size>
327 struct alignas(platform<>::maximum_vector_alignment) univector
328 : std::array<T, Size>,
329 univector_base<T, univector<T, Size>, is_vec_element<T>>
330 {
331 using std::array<T, Size>::size;
332 using size_type = size_t;
333 #if !defined CMT_COMPILER_MSVC || defined CMT_COMPILER_CLANG
univectorkfr::univector334 univector(univector& v) : univector(const_cast<const univector&>(v)) {}
335 #endif
336 univector(const univector& v) = default;
337 univector(univector&&) noexcept = default;
338 template <typename Input, KFR_ENABLE_IF(is_input_expression<Input>)>
univectorkfr::univector339 univector(Input&& input)
340 {
341 this->assign_expr(std::forward<Input>(input));
342 }
343 template <typename... Args>
univectorkfr::univector344 constexpr univector(const T& x, const Args&... args) CMT_NOEXCEPT
345 : std::array<T, Size>{ { x, static_cast<T>(args)... } }
346 {
347 }
348
349 constexpr univector() CMT_NOEXCEPT_SPEC(noexcept(std::array<T, Size>())) = default;
univectorkfr::univector350 constexpr univector(size_t, const T& value) { std::fill(this->begin(), this->end(), value); }
351 constexpr static bool size_known = true;
352 constexpr static bool is_array = true;
353 constexpr static bool is_array_ref = false;
354 constexpr static bool is_vector = false;
355 constexpr static bool is_aligned = true;
356 constexpr static bool is_pod = kfr::is_pod<T>;
357 using value_type = T;
358
getkfr::univector359 value_type get(size_t index, value_type fallback_value) const CMT_NOEXCEPT
360 {
361 return index < this->size() ? this->operator[](index) : fallback_value;
362 }
363 using univector_base<T, univector, is_vec_element<T>>::operator=;
364
resizekfr::univector365 void resize(size_t) CMT_NOEXCEPT {}
366 };
367
368 template <typename T>
369 struct univector<T, tag_array_ref> : array_ref<T>,
370 univector_base<T, univector<T, tag_array_ref>, is_vec_element<T>>
371 {
372 using array_ref<T>::size;
373 using array_ref<T>::array_ref;
374 using size_type = size_t;
375 #if !defined CMT_COMPILER_MSVC || defined CMT_COMPILER_CLANG
univectorkfr::univector376 univector(univector& v) : univector(const_cast<const univector&>(v)) {}
377 #endif
378 univector(const univector& v) = default;
379 univector(univector&&) noexcept = default;
univectorkfr::univector380 constexpr univector(const array_ref<T>& other) : array_ref<T>(other) {}
univectorkfr::univector381 constexpr univector(array_ref<T>&& other) : array_ref<T>(std::move(other)) {}
382
383 template <univector_tag Tag>
univectorkfr::univector384 constexpr univector(const univector<T, Tag>& other) : array_ref<T>(other.data(), other.size())
385 {
386 }
387 template <univector_tag Tag>
univectorkfr::univector388 constexpr univector(univector<T, Tag>& other) : array_ref<T>(other.data(), other.size())
389 {
390 }
391 template <typename U, univector_tag Tag, KFR_ENABLE_IF(is_same<remove_const<T>, U>&& is_const<T>)>
univectorkfr::univector392 constexpr univector(const univector<U, Tag>& other) : array_ref<T>(other.data(), other.size())
393 {
394 }
395 template <typename U, univector_tag Tag, KFR_ENABLE_IF(is_same<remove_const<T>, U>&& is_const<T>)>
univectorkfr::univector396 constexpr univector(univector<U, Tag>& other) : array_ref<T>(other.data(), other.size())
397 {
398 }
resizekfr::univector399 void resize(size_t) CMT_NOEXCEPT {}
400 constexpr static bool size_known = false;
401 constexpr static bool is_array = false;
402 constexpr static bool is_array_ref = true;
403 constexpr static bool is_vector = false;
404 constexpr static bool is_aligned = false;
405 using value_type = remove_const<T>;
406
getkfr::univector407 value_type get(size_t index, value_type fallback_value) const CMT_NOEXCEPT
408 {
409 return index < this->size() ? this->operator[](index) : fallback_value;
410 }
411 using univector_base<T, univector, is_vec_element<T>>::operator=;
412
refkfr::univector413 univector<T, tag_array_ref>& ref() && { return *this; }
414 };
415
416 template <typename T>
417 struct univector<T, tag_dynamic_vector>
418 : std::vector<T, allocator<T>>, univector_base<T, univector<T, tag_dynamic_vector>, is_vec_element<T>>
419 {
420 using std::vector<T, allocator<T>>::size;
421 using std::vector<T, allocator<T>>::vector;
422 using size_type = size_t;
423 #if !defined CMT_COMPILER_MSVC || defined CMT_COMPILER_CLANG
univectorkfr::univector424 univector(univector& v) : univector(const_cast<const univector&>(v)) {}
425 #endif
426 univector(const univector& v) = default;
427 univector(univector&&) noexcept = default;
428 template <typename Input, KFR_ENABLE_IF(is_input_expression<Input>)>
univectorkfr::univector429 univector(Input&& input)
430 {
431 static_assert(!is_infinite<Input>, "Dynamically sized vector requires finite input expression");
432 this->resize(input.size());
433 this->assign_expr(std::forward<Input>(input));
434 }
435 constexpr univector() CMT_NOEXCEPT_SPEC(noexcept(std::vector<T, allocator<T>>())) = default;
univectorkfr::univector436 constexpr univector(const std::vector<T, allocator<T>>& other) : std::vector<T, allocator<T>>(other) {}
univectorkfr::univector437 constexpr univector(std::vector<T, allocator<T>>&& other) : std::vector<T, allocator<T>>(std::move(other))
438 {
439 }
univectorkfr::univector440 constexpr univector(const array_ref<T>& other) : std::vector<T, allocator<T>>(other.begin(), other.end())
441 {
442 }
univectorkfr::univector443 constexpr univector(const array_ref<const T>& other)
444 : std::vector<T, allocator<T>>(other.begin(), other.end())
445 {
446 }
447 template <typename Allocator>
448 constexpr univector(const std::vector<T, Allocator>&) = delete;
449 template <typename Allocator>
450 constexpr univector(std::vector<T, Allocator>&&) = delete;
451 constexpr static bool size_known = false;
452 constexpr static bool is_array = false;
453 constexpr static bool is_array_ref = false;
454 constexpr static bool is_vector = true;
455 constexpr static bool is_aligned = true;
456 using value_type = T;
457
getkfr::univector458 value_type get(size_t index, value_type fallback_value) const CMT_NOEXCEPT
459 {
460 return index < this->size() ? this->operator[](index) : fallback_value;
461 }
462 using univector_base<T, univector, is_vec_element<T>>::operator=;
463 univector& operator=(const univector&) = default;
464 template <typename Input, KFR_ENABLE_IF(is_input_expression<Input>)>
operator =kfr::univector465 KFR_MEM_INTRINSIC univector& operator=(Input&& input)
466 {
467 if (input.size() != infinite_size)
468 this->resize(input.size());
469 this->assign_expr(std::forward<Input>(input));
470 return *this;
471 }
472 };
473
474 /// @brief Alias for ``univector<T, tag_array_ref>``;
475 template <typename T>
476 using univector_ref = univector<T, tag_array_ref>;
477
478 /// @brief Alias for ``univector<T, tag_dynamic_vector>``;
479 template <typename T>
480 using univector_dyn = univector<T, tag_dynamic_vector>;
481
482 template <typename T, univector_tag Size1 = tag_dynamic_vector, univector_tag Size2 = tag_dynamic_vector>
483 using univector2d = abstract_vector<univector<T, Size2>, Size1>;
484
485 template <typename T, univector_tag Size1 = tag_dynamic_vector, univector_tag Size2 = tag_dynamic_vector,
486 univector_tag Size3 = tag_dynamic_vector>
487 using univector3d = abstract_vector<abstract_vector<univector<T, Size3>, Size2>, Size1>;
488
489 /// @brief Creates univector from data and size
490 template <typename T>
make_univector(T * data,size_t size)491 KFR_INTRINSIC univector_ref<T> make_univector(T* data, size_t size)
492 {
493 return univector_ref<T>(data, size);
494 }
495
496 /// @brief Creates univector from data and size
497 template <typename T>
make_univector(const T * data,size_t size)498 KFR_INTRINSIC univector_ref<const T> make_univector(const T* data, size_t size)
499 {
500 return univector_ref<const T>(data, size);
501 }
502
503 /// @brief Creates univector from a container (must have data() and size() methods)
504 template <typename Container, KFR_ENABLE_IF(kfr::has_data_size<Container>),
505 typename T = value_type_of<Container>>
make_univector(const Container & container)506 KFR_INTRINSIC univector_ref<const T> make_univector(const Container& container)
507 {
508 return univector_ref<const T>(container.data(), container.size());
509 }
510
511 /// @brief Creates univector from a container (must have data() and size() methods)
512 template <typename Container, KFR_ENABLE_IF(kfr::has_data_size<Container>),
513 typename T = value_type_of<Container>>
make_univector(Container & container)514 KFR_INTRINSIC univector_ref<T> make_univector(Container& container)
515 {
516 return univector_ref<T>(container.data(), container.size());
517 }
518
519 /// @brief Creates univector from a sized array
520 template <typename T, size_t N>
make_univector(T (& arr)[N])521 KFR_INTRINSIC univector_ref<T> make_univector(T (&arr)[N])
522 {
523 return univector_ref<T>(arr, N);
524 }
525
526 /// @brief Creates univector from a sized array
527 template <typename T, size_t N>
make_univector(const T (& arr)[N])528 KFR_INTRINSIC univector_ref<const T> make_univector(const T (&arr)[N])
529 {
530 return univector_ref<const T>(arr, N);
531 }
532
533 /// @brief Single producer single consumer lock-free ring buffer
534 template <typename T>
535 struct lockfree_ring_buffer
536 {
lockfree_ring_bufferkfr::lockfree_ring_buffer537 lockfree_ring_buffer() : front(0), tail(0) {}
538
sizekfr::lockfree_ring_buffer539 size_t size() const
540 {
541 return tail.load(std::memory_order_relaxed) - front.load(std::memory_order_relaxed);
542 }
543
544 template <univector_tag Tag>
try_enqueuekfr::lockfree_ring_buffer545 size_t try_enqueue(const T* source, size_t size, univector<T, Tag>& buffer, bool partial = false)
546 {
547 const size_t cur_tail = tail.load(std::memory_order_relaxed);
548 const size_t avail_size = buffer.size() - (cur_tail - front.load(std::memory_order_relaxed));
549 if (size > avail_size)
550 {
551 if (!partial)
552 return 0;
553 size = std::min(size, avail_size);
554 }
555 std::atomic_thread_fence(std::memory_order_acquire);
556
557 const size_t real_tail = cur_tail % buffer.size();
558 const size_t first_size = std::min(buffer.size() - real_tail, size);
559 builtin_memcpy(buffer.data() + real_tail, source, first_size * sizeof(T));
560 builtin_memcpy(buffer.data(), source + first_size, (size - first_size) * sizeof(T));
561
562 std::atomic_thread_fence(std::memory_order_release);
563
564 tail.store(cur_tail + size, std::memory_order_relaxed);
565 return size;
566 }
567
568 template <univector_tag Tag>
try_dequeuekfr::lockfree_ring_buffer569 size_t try_dequeue(T* dest, size_t size, const univector<T, Tag>& buffer, bool partial = false)
570 {
571 const size_t cur_front = front.load(std::memory_order_relaxed);
572 const size_t avail_size = tail.load(std::memory_order_relaxed) - cur_front;
573 if (size > avail_size)
574 {
575 if (!partial)
576 return 0;
577 size = std::min(size, avail_size);
578 }
579 std::atomic_thread_fence(std::memory_order_acquire);
580
581 const size_t real_front = cur_front % buffer.size();
582 const size_t first_size = std::min(buffer.size() - real_front, size);
583 builtin_memcpy(dest, buffer.data() + real_front, first_size * sizeof(T));
584 builtin_memcpy(dest + first_size, buffer.data(), (size - first_size) * sizeof(T));
585
586 std::atomic_thread_fence(std::memory_order_release);
587
588 front.store(cur_front + size, std::memory_order_relaxed);
589 return size;
590 }
591
592 private:
593 std::atomic<size_t> front;
594 char cacheline_filler[64 - sizeof(std::atomic<size_t>)];
595 std::atomic<size_t> tail;
596 };
597 inline namespace CMT_ARCH_NAME
598 {
599
600 template <typename T, univector_tag Tag, typename U, size_t N>
get_elements(const univector<T,Tag> & self,cinput_t,size_t index,vec_shape<U,N>)601 KFR_INTRINSIC vec<U, N> get_elements(const univector<T, Tag>& self, cinput_t, size_t index, vec_shape<U, N>)
602 {
603 const T* data = self.data();
604 return static_cast<vec<U, N>>(read<N>(ptr_cast<T>(data) + index));
605 }
606
607 /// @brief Converts an expression to univector
608 template <typename Expr, typename T = value_type_of<Expr>>
render(Expr && expr)609 KFR_INTRINSIC univector<T> render(Expr&& expr)
610 {
611 static_assert(!is_infinite<Expr>,
612 "render: Can't process infinite expressions. Pass size as a second argument to render.");
613 univector<T> result;
614 result.resize(expr.size());
615 result = expr;
616 return result;
617 }
618
619 /// @brief Converts an expression to univector
620 template <typename Expr, typename T = value_type_of<Expr>>
render(Expr && expr,size_t size,size_t offset=0)621 KFR_INTRINSIC univector<T> render(Expr&& expr, size_t size, size_t offset = 0)
622 {
623 univector<T> result;
624 result.resize(size);
625 result = slice(expr, offset, size);
626 return result;
627 }
628
629 /// @brief Converts an expression to univector
630 template <typename Expr, size_t Size, typename T = value_type_of<Expr>>
render(Expr && expr,csize_t<Size>)631 KFR_INTRINSIC univector<T, Size> render(Expr&& expr, csize_t<Size>)
632 {
633 univector<T, Size> result;
634 result = expr;
635 return result;
636 }
637 } // namespace CMT_ARCH_NAME
638 } // namespace kfr
639
640 CMT_PRAGMA_MSVC(warning(pop))
641