UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
structure_of_arrays.hpp
1
42#ifndef UFO_CONTAINER_STRUCTURE_OF_ARRAYS_HPP
43#define UFO_CONTAINER_STRUCTURE_OF_ARRAYS_HPP
44
45// UFO
46#include <ufo/utility/type_traits.hpp>
47
48// STL
49#include <algorithm>
50#include <cstddef>
51#include <initializer_list>
52#include <ranges>
53#include <span>
54#include <stdexcept>
55#include <string>
56#include <tuple>
57#include <utility>
58#include <vector>
59
60namespace ufo
61{
62template <class... Ts>
63 requires(0 < sizeof...(Ts))
64class SoA;
65
66namespace detail
67{
68struct access {
69 template <class... Ts>
70 static constexpr auto const& data(SoA<Ts...> const& soa) noexcept
71 {
72 return soa.data_;
73 }
74
75 template <class... Ts>
76 static constexpr auto& data(SoA<Ts...>& soa) noexcept
77 {
78 return soa.data_;
79 }
80
81 template <class... Ts>
82 static constexpr auto data(SoA<Ts...>&& soa) noexcept
83 {
84 return std::move(soa.data_);
85 }
86};
87
88template <class T>
89struct is_soa : std::false_type {
90};
91
92template <class... Ts>
93struct is_soa<SoA<Ts...>> : std::true_type {
94};
95
96template <class T>
97struct is_vector : std::false_type {
98};
99
100template <class T, class Alloc>
101struct is_vector<std::vector<T, Alloc>> : std::true_type {
102};
103
104template <class T>
105struct is_span : std::false_type {
106};
107
108template <class T, std::size_t Extent>
109struct is_span<std::span<T, Extent>> : std::true_type {
110};
111
112template <class T>
113constexpr inline bool is_soa_v = is_soa<T>::value;
114
115template <class T>
116constexpr inline bool is_vector_v = is_vector<T>::value;
117
118template <class T>
119constexpr inline bool is_span_v = is_span<T>::value;
120
121template <class T>
122struct is_mergable : std::disjunction<is_soa<T>, is_vector<T>, is_span<T>> {
123};
124
125template <class T>
126constexpr inline bool is_mergable_v = is_mergable<T>::value;
127
128template <class T>
129 requires is_mergable_v<T>
131 using type = std::tuple<std::ranges::range_value_t<T>>;
132};
133
134template <class... Ts>
135struct extract_types<SoA<Ts...>> {
136 using type = std::tuple<Ts...>;
137};
138
139template <class T, class Alloc>
140struct extract_types<std::vector<T, Alloc>> {
141 using type = std::tuple<T>;
142};
143
144template <class T>
145using extract_types_t = typename extract_types<remove_cvref_t<T>>::type;
146
147template <class... Args>
148using merged_types_t = decltype(std::tuple_cat(std::declval<extract_types_t<Args>>()...));
149
150template <class T>
152
153template <class... Ts>
154struct tuple_to_soa<std::tuple<Ts...>> {
155 using type = SoA<Ts...>;
156};
157
158template <class... Args>
159using merged_soa_t = typename tuple_to_soa<merged_types_t<Args...>>::type;
160
161template <class... Ts>
162constexpr auto const& wrap_data(SoA<Ts...> const& soa) noexcept
163{
164 return access::data(soa);
165}
166
167template <class... Ts>
168constexpr auto& wrap_data(SoA<Ts...>& soa) noexcept
169{
170 return access::data(soa);
171}
172
173template <class... Ts>
174constexpr auto wrap_data(SoA<Ts...>&& soa) noexcept
175{
176 return access::data(std::move(soa));
177}
178
179template <class T, class Alloc>
180constexpr auto wrap_data(std::vector<T, Alloc> const& vec)
181{
182 return std::make_tuple(vec);
183}
184
185template <class T, class Alloc>
186constexpr auto wrap_data(std::vector<T, Alloc>& vec)
187{
188 return std::make_tuple(vec);
189}
190
191template <class T, class Alloc>
192constexpr auto wrap_data(std::vector<T, Alloc>&& vec)
193{
194 return std::make_tuple(std::move(vec));
195}
196
197template <class R>
198 requires is_mergable_v<remove_cvref_t<R>> && (!is_soa_v<remove_cvref_t<R>>) &&
199 (!is_vector_v<remove_cvref_t<R>>)
200constexpr auto wrap_data(R&& r)
201{
202 using T = std::ranges::range_value_t<R>;
203 return std::make_tuple(std::vector<T>(std::ranges::begin(r), std::ranges::end(r)));
204}
205} // namespace detail
206
207template <class... Ts>
208 requires(0 < sizeof...(Ts))
209class SoA
210{
211 public:
212 /**************************************************************************************
213 | |
214 | Tags |
215 | |
216 **************************************************************************************/
217
218 using data_type = std::tuple<std::vector<Ts>...>;
219
220 template <class... Us>
221 using zip_view_type = std::ranges::zip_view<std::ranges::ref_view<Us>...>;
222
223 using value_type = std::tuple<Ts...>;
224 using size_type = std::size_t;
225 using difference_type = std::ptrdiff_t;
226 using reference = std::tuple<Ts&...>;
227 using const_reference = std::tuple<Ts const&...>;
228 using iterator = std::ranges::iterator_t<zip_view_type<std::vector<Ts>...>>;
229 using const_iterator = std::ranges::iterator_t<zip_view_type<std::vector<Ts> const...>>;
230 using reverse_iterator = std::reverse_iterator<iterator>;
231 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
232
233 template <class... Us>
234 requires(0 < sizeof...(Us))
235 friend class SoA;
236
237 friend struct detail::access;
238
239 private:
240 using indices = std::index_sequence_for<Ts...>;
241
242 public:
243 /**************************************************************************************
244 | |
245 | Constructors |
246 | |
247 **************************************************************************************/
248
249 constexpr SoA() noexcept = default;
250
251 constexpr explicit SoA(size_type count) { resize(count); }
252
253 constexpr SoA(size_type count, value_type const& value) { resize(count, value); }
254
255 template <std::input_iterator InputIt>
256 constexpr SoA(InputIt first, InputIt last)
257 {
258 assign(first, last);
259 }
260
261 template <std::ranges::input_range R>
262 constexpr SoA(std::from_range_t, R&& range)
263 {
264 assign_range(std::forward<R>(range));
265 }
266
267 constexpr SoA(std::vector<Ts>... vecs) : data_(std::move(vecs)...) {}
268
269 constexpr SoA(SoA const& other) = default;
270
271 constexpr SoA(SoA&& other) noexcept = default;
272
273 constexpr SoA(std::initializer_list<value_type> init) : SoA(init.begin(), init.end()) {}
274
275 template <class... Args>
276 requires(0 < sizeof...(Args)) &&
277 (requires { detail::wrap_data(std::declval<Args>()); } && ...)
278 constexpr SoA(Args&&... args)
279 : data_(std::tuple_cat(detail::wrap_data(std::forward<Args>(args))...))
280 {
281 if constexpr (sizeof...(Args) > 1) {
282 size_type const s = size();
283 auto const check = [s](auto const& vec) {
284 if (vec.size() != s) {
285 throw std::invalid_argument("SoA: merged components must have the same size");
286 }
287 };
288 std::apply([&check](auto const&... vecs) { (check(vecs), ...); }, data_);
289 }
290 }
291
292 /**************************************************************************************
293 | |
294 | Destructor |
295 | |
296 **************************************************************************************/
297
298 constexpr ~SoA() = default;
299
300 /**************************************************************************************
301 | |
302 | Assignment operator |
303 | |
304 **************************************************************************************/
305
306 constexpr SoA& operator=(SoA const& rhs) = default;
307
308 constexpr SoA& operator=(SoA&& rhs) noexcept = default;
309
310 constexpr SoA& operator=(std::initializer_list<value_type> ilist)
311 {
312 assign(ilist);
313 return *this;
314 }
315
316 /**************************************************************************************
317 | |
318 | Assign |
319 | |
320 **************************************************************************************/
321
322 constexpr void assign(size_type count, value_type const& value)
323 {
324 assign_impl(count, value, indices{});
325 }
326
327 template <std::input_iterator I, std::sentinel_for<I> S>
328 constexpr void assign(I first, S last)
329 {
330 assign_range(std::ranges::subrange(first, last));
331 }
332
333 constexpr void assign(std::initializer_list<value_type> ilist)
334 {
335 assign(ilist.begin(), ilist.end());
336 }
337
338 template <std::ranges::input_range R>
339 requires(std::same_as<std::ranges::range_value_t<R>, value_type>)
340 constexpr void assign_range(R&& range)
341 {
342 assign_range_impl(std::forward<R>(range), std::make_index_sequence<sizeof...(Ts)>{});
343 }
344
345 /**************************************************************************************
346 | |
347 | View |
348 | |
349 **************************************************************************************/
350
351 template <class T>
352 [[nodiscard]] constexpr std::span<T> view()
353 {
354 return std::span{std::get<std::vector<T>>(data_)};
355 }
356
357 template <class T>
358 [[nodiscard]] constexpr std::span<T const> view() const
359 {
360 return std::span{std::get<std::vector<T>>(data_)};
361 }
362
363 template <std::size_t I>
364 [[nodiscard]] constexpr std::span<std::tuple_element_t<I, value_type>> view()
365 {
366 return std::span{std::get<I>(data_)};
367 }
368
369 template <std::size_t I>
370 [[nodiscard]] constexpr std::span<std::tuple_element_t<I, value_type> const> view()
371 const
372 {
373 return std::span{std::get<I>(data_)};
374 }
375
376 /**************************************************************************************
377 | |
378 | Element access |
379 | |
380 **************************************************************************************/
381
382 [[nodiscard]] constexpr reference operator[](size_type pos)
383 {
384 return operator[](pos, indices{});
385 }
386
387 [[nodiscard]] constexpr const_reference operator[](size_type pos) const
388 {
389 return operator[](pos, indices{});
390 }
391
392 [[nodiscard]] constexpr reference at(size_type pos)
393 {
394 check_index(pos);
395 return operator[](pos);
396 }
397
398 [[nodiscard]] constexpr const_reference at(size_type pos) const
399 {
400 check_index(pos);
401 return operator[](pos);
402 }
403
404 [[nodiscard]] constexpr reference front() { return operator[](0u); }
405
406 [[nodiscard]] constexpr const_reference front() const { return operator[](0u); }
407
408 [[nodiscard]] constexpr reference back() { return operator[](size() - 1); }
409
410 [[nodiscard]] constexpr const_reference back() const { return operator[](size() - 1); }
411
412 [[nodiscard]] constexpr data_type* data() noexcept { return &data_; }
413
414 [[nodiscard]] constexpr data_type const* data() const noexcept { return &data_; }
415
416 /**************************************************************************************
417 | |
418 | Iterators |
419 | |
420 **************************************************************************************/
421
422 [[nodiscard]] constexpr iterator begin() noexcept { return get_view().begin(); }
423
424 [[nodiscard]] constexpr const_iterator begin() const noexcept
425 {
426 return get_view().begin();
427 }
428
429 [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); }
430
431 [[nodiscard]] constexpr iterator end() noexcept { return get_view().end(); }
432
433 [[nodiscard]] constexpr const_iterator end() const noexcept { return get_view().end(); }
434
435 [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); }
436
437 [[nodiscard]] constexpr reverse_iterator rbegin() noexcept
438 {
439 return reverse_iterator(end());
440 }
441
442 [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept
443 {
444 return const_reverse_iterator(end());
445 }
446
447 [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept
448 {
449 return rbegin();
450 }
451
452 [[nodiscard]] constexpr reverse_iterator rend() noexcept
453 {
454 return reverse_iterator(begin());
455 }
456
457 [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept
458 {
459 return const_reverse_iterator(begin());
460 }
461
462 [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); }
463
464 /**************************************************************************************
465 | |
466 | Capacity |
467 | |
468 **************************************************************************************/
469
470 [[nodiscard]] constexpr bool empty() const noexcept
471 {
472 return std::get<0>(data_).empty();
473 }
474
475 [[nodiscard]] constexpr size_type size() const noexcept
476 {
477 return std::get<0>(data_).size();
478 }
479
480 [[nodiscard]] constexpr size_type max_size() const noexcept
481 {
482 return max_size_impl(indices{});
483 }
484
485 constexpr void reserve(size_type new_cap) { reserve(new_cap, indices{}); }
486
487 [[nodiscard]] constexpr size_type capacity() const noexcept
488 {
489 return capacity_impl(indices{});
490 }
491
492 constexpr void shrink_to_fit() { shrink_to_fit_impl(indices{}); }
493
494 /**************************************************************************************
495 | |
496 | Modifiers |
497 | |
498 **************************************************************************************/
499
500 constexpr void clear() noexcept { clear_impl(indices{}); }
501
502 constexpr iterator insert(iterator pos, value_type const& value)
503 {
504 return insert_impl(pos, value, indices{});
505 }
506
507 constexpr iterator insert(iterator pos, value_type&& value)
508 {
509 return insert_impl(pos, std::move(value), indices{});
510 }
511
512 constexpr iterator insert(iterator pos, size_type count, value_type const& value)
513 {
514 return insert_impl(pos, count, value, indices{});
515 }
516
517 template <std::input_iterator I, std::sentinel_for<I> S>
518 constexpr iterator insert(iterator pos, I first, S last)
519 {
520 return insert_impl(pos, first, last, indices{});
521 }
522
523 constexpr iterator insert(iterator pos, std::initializer_list<value_type> ilist)
524 {
525 return insert(pos, ilist.begin(), ilist.end());
526 }
527
528 template <std::ranges::input_range R>
529 constexpr iterator insert_range(iterator pos, R&& rg)
530 {
531 return insert_range_impl(pos, std::forward<R>(rg), indices{});
532 }
533
534 template <class... Us>
535 constexpr iterator emplace(iterator pos, Us&&... us)
536 {
537 static_assert(sizeof...(Us) == sizeof...(Ts), "SoA: invalid number of arguments");
538 std::size_t const idx = std::distance(begin(), pos);
539 emplace_impl(pos, std::forward<Us>(us)...);
540 return begin() + idx;
541 }
542
543 constexpr iterator erase(iterator pos) { return erase_impl(pos, indices{}); }
544
545 constexpr iterator erase(iterator first, iterator last)
546 {
547 return erase_impl(first, last, indices{});
548 }
549
550 constexpr void push_back(value_type const& value) { push_back_impl(value, indices{}); }
551
552 constexpr void push_back(value_type&& value)
553 {
554 push_back_impl(std::move(value), indices{});
555 }
556
557 template <class... Us>
558 constexpr void push_back(Us&&... us)
559 {
560 emplace_back(std::forward<Us>(us)...);
561 }
562
563 // TODO: Check so each Us is of type Ts
564 template <class... Us>
565 constexpr reference emplace_back(Us&&... us)
566 {
567 static_assert(sizeof...(Us) == sizeof...(Ts), "SoA: invalid number of arguments");
568 emplace_back_impl(std::forward<Us>(us)...);
569 return back();
570 }
571
572 template <std::ranges::input_range R>
573 constexpr void append_range(R&& rg)
574 {
575 append_range_impl(std::forward<R>(rg), indices{});
576 }
577
578 constexpr void pop_back() { pop_back_impl(indices{}); }
579
580 constexpr void resize(size_type count) { resize_impl(count, indices{}); }
581
582 constexpr void resize(size_type count, value_type const& value)
583 {
584 resize_impl(count, value, indices{});
585 }
586
587 constexpr void swap(SoA& other) noexcept { swap_impl(other, indices{}); }
588
589 friend constexpr void swap(SoA& lhs, SoA& rhs) noexcept { lhs.swap(rhs); }
590
591 template <std::size_t I>
592 [[nodiscard]] constexpr std::tuple_element_t<I, value_type>& get(size_type pos)
593 {
594 return std::get<I>(data_)[pos];
595 }
596
597 template <std::size_t I>
598 [[nodiscard]] constexpr std::tuple_element_t<I, value_type> const& get(
599 size_type pos) const
600 {
601 return std::get<I>(data_)[pos];
602 }
603
604 template <class T>
605 [[nodiscard]] constexpr T& get(size_type pos)
606 {
607 static_assert(contains_type_v<T, Ts...>, "SoA: type not found");
608 static_assert(is_unique_v<Ts...>, "SoA: type is not unique, use get<I>(pos) instead");
609 return std::get<index_v<T, Ts...>>(data_)[pos];
610 }
611
612 template <class T>
613 [[nodiscard]] constexpr T const& get(size_type pos) const
614 {
615 static_assert(contains_type_v<T, Ts...>, "SoA: type not found");
616 static_assert(is_unique_v<Ts...>, "SoA: type is not unique, use get<I>(pos) instead");
617 return std::get<index_v<T, Ts...>>(data_)[pos];
618 }
619
620 private:
621 constexpr void check_index(size_type pos) const
622 {
623 if (size() <= pos) {
624 throw std::out_of_range("SoA::at: index " + std::to_string(pos) +
625 " is out of range for size " + std::to_string(size()));
626 }
627 }
628
629 template <std::size_t... Is>
630 constexpr void assign_impl(size_type count, value_type const& value,
631 std::index_sequence<Is...>)
632 {
633 (std::get<Is>(data_).assign(count, std::get<Is>(value)), ...);
634 }
635
636 template <std::ranges::input_range R, std::size_t... Is>
637 constexpr void assign_range_impl(R&& range, std::index_sequence<Is...>)
638 {
639 (std::get<Is>(data_).assign(std::views::elements<Is>(range).begin(),
640 std::views::elements<Is>(range).end()),
641 ...);
642 }
643
644 template <std::size_t... Is>
645 [[nodiscard]] constexpr reference operator[](size_type pos, std::index_sequence<Is...>)
646 {
647 return std::tie(std::get<Is>(data_)[pos]...);
648 }
649
650 template <std::size_t... Is>
651 [[nodiscard]] constexpr const_reference operator[](size_type pos,
652 std::index_sequence<Is...>) const
653 {
654 return std::tie(std::get<Is>(data_)[pos]...);
655 }
656
657 template <std::size_t... Is>
658 [[nodiscard]] constexpr size_type max_size_impl(
659 std::index_sequence<Is...>) const noexcept
660 {
661 return std::min({std::get<Is>(data_).max_size()...});
662 }
663
664 template <std::size_t... Is>
665 constexpr void reserve_impl(size_type new_cap, std::index_sequence<Is...>)
666 {
667 (std::get<Is>(data_).reserve(new_cap), ...);
668 }
669
670 template <std::size_t... Is>
671 [[nodiscard]] constexpr size_type capacity_impl(
672 std::index_sequence<Is...>) const noexcept
673 {
674 return std::min({std::get<Is>(data_).capacity()...});
675 }
676
677 template <std::size_t... Is>
678 constexpr void shrink_to_fit_impl(std::index_sequence<Is...>)
679 {
680 (std::get<Is>(data_).shrink_to_fit(), ...);
681 }
682
683 template <std::size_t... Is>
684 constexpr void clear_impl(std::index_sequence<Is...>) noexcept
685 {
686 (std::get<Is>(data_).clear(), ...);
687 }
688
689 template <std::size_t... Is>
690 constexpr iterator insert_impl(iterator pos, value_type const& value,
691 std::index_sequence<Is...>)
692 {
693 std::size_t const idx = std::distance(begin(), pos);
694 (std::get<Is>(data_).insert(std::get<Is>(data_).begin() + idx, std::get<Is>(value)),
695 ...);
696 return begin() + idx;
697 }
698
699 template <std::size_t... Is>
700 constexpr iterator insert_impl(iterator pos, value_type&& value,
701 std::index_sequence<Is...>)
702 {
703 std::size_t const idx = std::distance(begin(), pos);
704 (std::get<Is>(data_).insert(std::get<Is>(data_).begin() + idx,
705 std::move(std::get<Is>(value))),
706 ...);
707 return begin() + idx;
708 }
709
710 template <std::size_t... Is>
711 constexpr iterator insert_impl(iterator pos, size_type count, value_type const& value,
712 std::index_sequence<Is...>)
713 {
714 std::size_t const idx = std::distance(begin(), pos);
715 (std::get<Is>(data_).insert(std::get<Is>(data_).begin() + idx, count,
716 std::get<Is>(value)),
717 ...);
718 return begin() + idx;
719 }
720
721 template <std::input_iterator I, std::sentinel_for<I> S, std::size_t... Is>
722 constexpr iterator insert_impl(iterator pos, I first, S last,
723 std::index_sequence<Is...>)
724 {
725 std::size_t const idx = std::distance(begin(), pos);
726 auto rg = std::ranges::subrange(first, last);
727 (std::get<Is>(data_).insert(std::get<Is>(data_).begin() + idx,
728 std::views::elements<Is>(rg).begin(),
729 std::views::elements<Is>(rg).end()),
730 ...);
731 return begin() + idx;
732 }
733
734 template <std::ranges::input_range R, std::size_t... Is>
735 constexpr iterator insert_range_impl(iterator pos, R&& rg, std::index_sequence<Is...>)
736 {
737 std::size_t const idx = std::distance(begin(), pos);
738 (std::get<Is>(data_).insert(std::get<Is>(data_).begin() + idx,
739 std::views::elements<Is>(rg).begin(),
740 std::views::elements<Is>(rg).end()),
741 ...);
742 return begin() + idx;
743 }
744
745 template <class... Us>
746 constexpr void emplace_impl(iterator pos, Us&&... us)
747 {
748 emplace_impl(pos, indices{}, std::forward<Us>(us)...);
749 }
750
751 template <std::size_t... Is, class... Us>
752 constexpr void emplace_impl(iterator pos, std::index_sequence<Is...>, Us&&... us)
753 {
754 std::size_t const idx = std::distance(begin(), pos);
755 (std::get<Is>(data_).emplace(std::get<Is>(data_).begin() + idx, std::forward<Us>(us)),
756 ...);
757 }
758
759 template <std::size_t... Is>
760 constexpr iterator erase_impl(iterator pos, std::index_sequence<Is...>)
761 {
762 auto const idx = std::distance(begin(), pos);
763 (std::get<Is>(data_).erase(std::get<Is>(data_).begin() + idx), ...);
764 return begin() + idx;
765 }
766
767 template <std::size_t... Is>
768 constexpr iterator erase_impl(iterator first, iterator last, std::index_sequence<Is...>)
769 {
770 auto const f_idx = std::distance(begin(), first);
771 auto const l_idx = std::distance(begin(), last);
772 (std::get<Is>(data_).erase(std::get<Is>(data_).begin() + f_idx,
773 std::get<Is>(data_).begin() + l_idx),
774 ...);
775 return begin() + f_idx;
776 }
777
778 template <std::size_t... Is>
779 constexpr void push_back_impl(value_type const& value, std::index_sequence<Is...>)
780 {
781 (std::get<Is>(data_).push_back(std::get<Is>(value)), ...);
782 }
783
784 template <std::size_t... Is>
785 constexpr void push_back_impl(value_type&& value, std::index_sequence<Is...>)
786 {
787 (std::get<Is>(data_).push_back(std::move(std::get<Is>(value))), ...);
788 }
789
790 template <class... Us>
791 constexpr void emplace_back_impl(Us&&... us)
792 {
793 emplace_back_impl(indices{}, std::forward<Us>(us)...);
794 }
795
796 template <std::size_t... Is, class... Us>
797 constexpr void emplace_back_impl(std::index_sequence<Is...>, Us&&... us)
798 {
799 (std::get<Is>(data_).emplace_back(std::forward<Us>(us)), ...);
800 }
801
802 template <std::ranges::input_range R, std::size_t... Is>
803 constexpr void append_range_impl(R&& range, std::index_sequence<Is...>)
804 {
805 (std::get<Is>(data_).append_range(std::views::elements<Is>(range)), ...);
806 }
807
808 template <std::size_t... Is>
809 constexpr void pop_back_impl(std::index_sequence<Is...>)
810 {
811 (std::get<Is>(data_).pop_back(), ...);
812 }
813
814 template <std::size_t... Is>
815 constexpr void resize_impl(size_type count, std::index_sequence<Is...>)
816 {
817 (std::get<Is>(data_).resize(count), ...);
818 }
819
820 template <std::size_t... Is>
821 constexpr void resize_impl(size_type count, value_type const& value,
822 std::index_sequence<Is...>)
823 {
824 (std::get<Is>(data_).resize(count, std::get<Is>(value)), ...);
825 }
826
827 template <std::size_t... Is>
828 constexpr void swap_impl(SoA& other, std::index_sequence<Is...>) noexcept
829 {
830 (std::get<Is>(data_).swap(std::get<Is>(other.data_)), ...);
831 }
832
833 [[nodiscard]] constexpr auto get_view() noexcept { return get_view(indices{}); }
834
835 [[nodiscard]] constexpr auto get_view() const noexcept { return get_view(indices{}); }
836
837 template <std::size_t... Is>
838 [[nodiscard]] constexpr auto get_view(std::index_sequence<Is...>) noexcept
839 {
840 return std::views::zip(std::ranges::ref_view(std::get<Is>(data_))...);
841 }
842
843 template <std::size_t... Is>
844 [[nodiscard]] constexpr auto get_view(std::index_sequence<Is...>) const noexcept
845 {
846 return std::views::zip(std::ranges::ref_view(std::get<Is>(data_))...);
847 }
848
849 data_type data_;
850};
851
852template <class... Ts>
853SoA(std::initializer_list<std::tuple<Ts...>>) -> SoA<Ts...>;
854
855template <class... Ts>
856SoA(std::size_t, std::tuple<Ts...>) -> SoA<Ts...>;
857
858template <class... Ts>
859SoA(std::vector<Ts>...) -> SoA<Ts...>;
860
861// 2-argument merging
862template <class... Ts, class... Us>
863SoA(SoA<Ts...>, SoA<Us...>) -> SoA<Ts..., Us...>;
864
865template <class... Ts, class U, class Alloc>
866SoA(SoA<Ts...>, std::vector<U, Alloc>) -> SoA<Ts..., U>;
867
868template <class T, class Alloc, class... Us>
869SoA(std::vector<T, Alloc>, SoA<Us...>) -> SoA<T, Us...>;
870
871template <class T1, class Alloc1, class T2, class Alloc2>
872SoA(std::vector<T1, Alloc1>, std::vector<T2, Alloc2>) -> SoA<T1, T2>;
873
874// 3-argument merging
875template <class... Ts, class... Us, class... Vs>
876SoA(SoA<Ts...>, SoA<Us...>, SoA<Vs...>) -> SoA<Ts..., Us..., Vs...>;
877
878template <class... Ts, class... Us, class V, class Alloc>
879SoA(SoA<Ts...>, SoA<Us...>, std::vector<V, Alloc>) -> SoA<Ts..., Us..., V>;
880
881template <class... Ts, class U, class Alloc, class... Vs>
882SoA(SoA<Ts...>, std::vector<U, Alloc>, SoA<Vs...>) -> SoA<Ts..., U, Vs...>;
883
884template <class T, class Alloc, class... Us, class... Vs>
885SoA(std::vector<T, Alloc>, SoA<Us...>, SoA<Vs...>) -> SoA<T, Us..., Vs...>;
886
887template <class... Ts, class U1, class Alloc1, class U2, class Alloc2>
888SoA(SoA<Ts...>, std::vector<U1, Alloc1>, std::vector<U2, Alloc2>) -> SoA<Ts..., U1, U2>;
889
890template <class T1, class Alloc1, class... Us, class T2, class Alloc2>
891SoA(std::vector<T1, Alloc1>, SoA<Us...>, std::vector<T2, Alloc2>) -> SoA<T1, Us..., T2>;
892
893template <class T1, class Alloc1, class T2, class Alloc2, class... Vs>
894SoA(std::vector<T1, Alloc1>, std::vector<T2, Alloc2>, SoA<Vs...>) -> SoA<T1, T2, Vs...>;
895
896template <class T1, class Alloc1, class T2, class Alloc2, class T3, class Alloc3>
897SoA(std::vector<T1, Alloc1>, std::vector<T2, Alloc2>, std::vector<T3, Alloc3>)
899
900//
901// Non-member functions
902//
903
904template <class... Ts>
905[[nodiscard]] constexpr auto begin(SoA<Ts...>& c)
906{
907 return c.begin();
908}
909
910template <class... Ts>
911[[nodiscard]] constexpr auto begin(SoA<Ts...> const& c)
912{
913 return c.begin();
914}
915
916template <class... Ts>
917[[nodiscard]] constexpr auto end(SoA<Ts...>& c)
918{
919 return c.end();
920}
921
922template <class... Ts>
923[[nodiscard]] constexpr auto end(SoA<Ts...> const& c)
924{
925 return c.end();
926}
927
928template <class... Ts>
929[[nodiscard]] constexpr auto size(SoA<Ts...> const& c)
930{
931 return c.size();
932}
933
934template <class... Ts, class U>
935constexpr typename SoA<Ts...>::size_type erase(SoA<Ts...>& c, U const& value)
936{
937 auto it = std::remove(c.begin(), c.end(), value);
938 auto r = std::distance(it, c.end());
939 c.erase(it, c.end());
940 return r;
941}
942
943template <class... Ts, class Pred>
944constexpr typename SoA<Ts...>::size_type erase_if(SoA<Ts...>& c, Pred pred)
945{
946 auto it = std::remove_if(c.begin(), c.end(), pred);
947 auto r = std::distance(it, c.end());
948 c.erase(it, c.end());
949 return r;
950}
951
952template <class... Ts>
953[[nodiscard]] constexpr bool operator==(SoA<Ts...> const& lhs, SoA<Ts...> const& rhs)
954{
955 return lhs.size() == rhs.size() && std::ranges::equal(lhs, rhs);
956}
957
958template <class... Ts>
959[[nodiscard]] constexpr auto operator<=>(SoA<Ts...> const& lhs, SoA<Ts...> const& rhs)
960{
961 return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(),
962 rhs.end());
963}
964
965template <class... Args>
966 requires(0 < sizeof...(Args)) && (detail::is_soa_v<remove_cvref_t<Args>> || ...) &&
967 (requires { detail::wrap_data(std::declval<Args>()); } && ...)
968[[nodiscard]] constexpr auto merge(Args&&... args)
969{
970 return detail::merged_soa_t<Args...>(std::forward<Args>(args)...);
971}
972
973//
974// Type traits
975//
976
977template <class T, class... Ts>
978struct contains_type<T, SoA<Ts...>> : contains_type<T, Ts...> {
979};
980
981template <class T, class... Ts>
982[[nodiscard]] constexpr auto view(SoA<Ts...>& soa)
983{
984 return soa.template view<T>();
985}
986
987template <class T, class... Ts>
988[[nodiscard]] constexpr auto view(SoA<Ts...> const& soa)
989{
990 return soa.template view<T>();
991}
992
993template <std::size_t I, class... Ts>
994[[nodiscard]] constexpr auto view(SoA<Ts...>& soa)
995{
996 return soa.template view<I>();
997}
998
999template <std::size_t I, class... Ts>
1000[[nodiscard]] constexpr auto view(SoA<Ts...> const& soa)
1001{
1002 return soa.template view<I>();
1003}
1004} // namespace ufo
1005
1006#endif // UFO_CONTAINER_STRUCTURE_OF_ARRAYS_HPP
constexpr Vec< Cols, T > & get(Mat< Rows, Cols, T > &m) noexcept
Structured-binding accessor — lvalue reference to row I.
Definition mat.hpp:1586
STL namespace.
All vision-related classes and functions.
Definition cloud.hpp:49