xtensor
Loading...
Searching...
No Matches
xutils.hpp
1/***************************************************************************
2 * Copyright (c) Johan Mabille, Sylvain Corlay and Wolf Vollprecht *
3 * Copyright (c) QuantStack *
4 * *
5 * Distributed under the terms of the BSD 3-Clause License. *
6 * *
7 * The full license is in the file LICENSE, distributed with this software. *
8 ****************************************************************************/
9
10#ifndef XTENSOR_UTILS_HPP
11#define XTENSOR_UTILS_HPP
12
13#include <algorithm>
14#include <array>
15#include <cstddef>
16#include <initializer_list>
17#include <iostream>
18#include <iterator>
19#include <memory>
20#include <tuple>
21#include <type_traits>
22#include <utility>
23#include <vector>
24
25#include <xtl/xfunctional.hpp>
26#include <xtl/xmeta_utils.hpp>
27#include <xtl/xsequence.hpp>
28#include <xtl/xtype_traits.hpp>
29
30#include "../core/xtensor_config.hpp"
31
32namespace xt
33{
34 /****************
35 * declarations *
36 ****************/
37
38 template <class T>
39 struct remove_class;
40
41 /*template <class F, class... T>
42 void for_each(F&& f, std::tuple<T...>& t) noexcept(implementation_dependent);*/
43
44 /*template <class F, class R, class... T>
45 R accumulate(F&& f, R init, const std::tuple<T...>& t) noexcept(implementation_dependent);*/
46
47 template <std::size_t I, class... Args>
48 constexpr decltype(auto) argument(Args&&... args) noexcept;
49
50 template <class R, class F, class... S>
51 R apply(std::size_t index, F&& func, const std::tuple<S...>& s) noexcept(noexcept(func(std::get<0>(s))));
52
53 template <class T, class S>
54 void nested_copy(T&& iter, const S& s);
55
56 template <class T, class S>
57 void nested_copy(T&& iter, std::initializer_list<S> s);
58
59 template <class C>
60 bool resize_container(C& c, typename C::size_type size);
61
62 template <class T, std::size_t N>
63 bool resize_container(std::array<T, N>& a, typename std::array<T, N>::size_type size);
64
65 template <std::size_t... I>
66 class fixed_shape;
67
68 template <std::size_t... I>
69 bool resize_container(fixed_shape<I...>& a, std::size_t size);
70
71 template <class X, class C>
73
74 template <class X, class C>
75 using rebind_container_t = typename rebind_container<X, C>::type;
76
77 std::size_t normalize_axis(std::size_t dim, std::ptrdiff_t axis);
78
79 // gcc 4.9 is affected by C++14 defect CGW 1558
80 // see http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1558
81 template <class... T>
82 struct make_void
83 {
84 using type = void;
85 };
86
87 template <class... T>
88 using void_t = typename make_void<T...>::type;
89
90 // This is used for non existent types (e.g. storage for some expressions
91 // like generators)
93 {
94 };
95
96 template <class... T>
98 {
99 using type = invalid_type;
100 };
101
102 template <class T, class R>
103 using disable_integral_t = std::enable_if_t<!xtl::is_integral<T>::value, R>;
104
105 /***************************************
106 * is_specialization_of implementation *
107 ***************************************/
108
109 template <template <class...> class TT, class T>
110 struct is_specialization_of : std::false_type
111 {
112 };
113
114 template <template <class...> class TT, class... Ts>
115 struct is_specialization_of<TT, TT<Ts...>> : std::true_type
116 {
117 };
118
119 /*******************************
120 * remove_class implementation *
121 *******************************/
122
123 template <class T>
125 {
126 };
127
128 template <class C, class R, class... Args>
129 struct remove_class<R (C::*)(Args...)>
130 {
131 typedef R type(Args...);
132 };
133
134 template <class C, class R, class... Args>
135 struct remove_class<R (C::*)(Args...) const>
136 {
137 typedef R type(Args...);
138 };
139
140 template <class T>
141 using remove_class_t = typename remove_class<T>::type;
142
143 /***************************
144 * for_each implementation *
145 ***************************/
146
147 namespace detail
148 {
149 template <class F, size_t... I, class... Ts>
150 void for_each(F&& f, std::tuple<Ts...>& t, std::index_sequence<I...>) noexcept(
151 (noexcept(f(std::get<I>(t))) && ...)
152 )
153 {
154 (f(std::get<I>(t)), ...);
155 }
156
157 template <class F, size_t... I, class... Ts>
158 void for_each(F&& f, const std::tuple<Ts...>& t, std::index_sequence<I...>) noexcept(
159 (noexcept(f(std::get<I>(t))) && ...)
160 )
161 {
162 (f(std::get<I>(t)), ...);
163 }
164 }
165
166 template <class F, class... Ts>
167 inline void for_each(F&& f, std::tuple<Ts...>& t) noexcept(
168 noexcept(detail::for_each(std::forward<F>(f), t, std::make_index_sequence<sizeof...(Ts)>{}))
169 )
170 {
171 detail::for_each(std::forward<F>(f), t, std::make_index_sequence<sizeof...(Ts)>{});
172 }
173
174 template <class F, class... Ts>
175 inline void for_each(F&& f, const std::tuple<Ts...>& t) noexcept(
176 noexcept(detail::for_each(std::forward<F>(f), t, std::make_index_sequence<sizeof...(Ts)>{}))
177 )
178 {
179 detail::for_each(std::forward<F>(f), t, std::make_index_sequence<sizeof...(Ts)>{});
180 }
181
182 /*****************************
183 * accumulate implementation *
184 *****************************/
185
187
188 namespace detail
189 {
190 template <class F, class R, class... T, size_t... I>
191 R accumulate_impl(F&& f, R init, const std::tuple<T...>& t, std::index_sequence<I...> /*I*/) noexcept(
192 (noexcept(f(init, std::get<I>(t))) && ...)
193 )
194 {
195 R res = init;
196 auto wrapper = [&](const auto& i, const auto& j)
197 {
198 res = f(i, j);
199 };
200 (wrapper(res, std::get<I>(t)), ...);
201 return res;
202 }
203 }
204
205 template <class F, class R, class... T>
206 inline R accumulate(F&& f, R init, const std::tuple<T...>& t) noexcept(
207 noexcept(detail::accumulate_impl(std::forward<F>(f), init, t, std::make_index_sequence<sizeof...(T)>{}))
208 )
209 {
210 return detail::accumulate_impl(std::forward<F>(f), init, t, std::make_index_sequence<sizeof...(T)>{});
211 }
212
214
215 /***************************
216 * argument implementation *
217 ***************************/
218
219 namespace detail
220 {
221 template <std::size_t I>
222 struct getter
223 {
224 template <class Arg, class... Args>
225 static constexpr decltype(auto) get(Arg&& /*arg*/, Args&&... args) noexcept
226 {
227 return getter<I - 1>::get(std::forward<Args>(args)...);
228 }
229 };
230
231 template <>
232 struct getter<0>
233 {
234 template <class Arg, class... Args>
235 static constexpr Arg&& get(Arg&& arg, Args&&... /*args*/) noexcept
236 {
237 return std::forward<Arg>(arg);
238 }
239 };
240 }
241
242 template <std::size_t I, class... Args>
243 constexpr decltype(auto) argument(Args&&... args) noexcept
244 {
245 static_assert(I < sizeof...(Args), "I should be lesser than sizeof...(Args)");
246 return detail::getter<I>::get(std::forward<Args>(args)...);
247 }
248
249 /************************
250 * apply implementation *
251 ************************/
252
253 template <class R, class F, class... S>
254 inline R
255 apply(std::size_t index, F&& func, const std::tuple<S...>& s) noexcept(noexcept(func(std::get<0>(s))))
256 {
257 XTENSOR_ASSERT(sizeof...(S) > index);
258 return std::apply(
259 [&](const S&... args) -> R
260 {
261 auto f_impl = [&](auto&& self, auto&& i, auto&& h, auto&&... t) -> R
262 {
263 if (i == index)
264 {
265 return static_cast<R>(func(h));
266 }
267 if constexpr (sizeof...(t) > 0)
268 {
269 return self(self, std::size_t{i + 1}, t...);
270 }
271 return R{};
272 };
273 return f_impl(f_impl, std::size_t{0}, args...);
274 },
275 s
276 );
277 }
278
279 /***************************
280 * nested_initializer_list *
281 ***************************/
282
283 template <class T, std::size_t I>
285 {
286 using type = std::initializer_list<typename nested_initializer_list<T, I - 1>::type>;
287 };
288
289 template <class T>
291 {
292 using type = T;
293 };
294
295 template <class T, std::size_t I>
296 using nested_initializer_list_t = typename nested_initializer_list<T, I>::type;
297
298 /******************************
299 * nested_copy implementation *
300 ******************************/
301
302 template <class T, class S>
303 inline void nested_copy(T&& iter, const S& s)
304 {
305 *iter++ = s;
306 }
307
308 template <class T, class S>
309 inline void nested_copy(T&& iter, std::initializer_list<S> s)
310 {
311 for (auto it = s.begin(); it != s.end(); ++it)
312 {
313 nested_copy(std::forward<T>(iter), *it);
314 }
315 }
316
317 /***********************************
318 * resize_container implementation *
319 ***********************************/
320 template <class C>
321 inline bool resize_container(C& c, typename C::size_type size)
322 {
323 c.resize(size);
324 return true;
325 }
326
327 template <class T, std::size_t N>
328 inline bool resize_container(std::array<T, N>& /*a*/, typename std::array<T, N>::size_type size)
329 {
330 return size == N;
331 }
332
333 template <std::size_t... I>
334 inline bool resize_container(xt::fixed_shape<I...>&, std::size_t size)
335 {
336 return sizeof...(I) == size;
337 }
338
339 /*********************************
340 * normalize_axis implementation *
341 *********************************/
342
343 // scalar normalize axis
344 inline std::size_t normalize_axis(std::size_t dim, std::ptrdiff_t axis)
345 {
346 return axis < 0 ? static_cast<std::size_t>(static_cast<std::ptrdiff_t>(dim) + axis)
347 : static_cast<std::size_t>(axis);
348 }
349
350 template <class E, class C>
351 inline std::enable_if_t<
352 !xtl::is_integral<std::decay_t<C>>::value && xtl::is_signed<typename std::decay_t<C>::value_type>::value,
353 rebind_container_t<std::size_t, std::decay_t<C>>>
354 normalize_axis(E& expr, C&& axes)
355 {
356 rebind_container_t<std::size_t, std::decay_t<C>> res;
357 resize_container(res, axes.size());
358
359 for (std::size_t i = 0; i < axes.size(); ++i)
360 {
361 res[i] = normalize_axis(expr.dimension(), axes[i]);
362 }
363
364 XTENSOR_ASSERT(std::all_of(
365 res.begin(),
366 res.end(),
367 [&expr](auto ax_el)
368 {
369 return ax_el < expr.dimension();
370 }
371 ));
372
373 return res;
374 }
375
376 template <class C, class E>
377 inline std::enable_if_t<
378 !xtl::is_integral<std::decay_t<C>>::value && std::is_unsigned<typename std::decay_t<C>::value_type>::value,
379 C&&>
380 normalize_axis(E& expr, C&& axes)
381 {
382 static_cast<void>(expr);
383 XTENSOR_ASSERT(std::all_of(
384 axes.begin(),
385 axes.end(),
386 [&expr](auto ax_el)
387 {
388 return ax_el < expr.dimension();
389 }
390 ));
391 return std::forward<C>(axes);
392 }
393
394 template <class R, class E, class C>
395 inline auto forward_normalize(E& expr, C&& axes)
396 -> std::enable_if_t<xtl::is_signed<std::decay_t<decltype(*std::begin(axes))>>::value, R>
397 {
398 R res;
399 xt::resize_container(res, std::size(axes));
400 auto dim = expr.dimension();
401 std::transform(
402 std::begin(axes),
403 std::end(axes),
404 std::begin(res),
405 [&dim](auto ax_el)
406 {
407 return normalize_axis(dim, ax_el);
408 }
409 );
410
411 XTENSOR_ASSERT(std::all_of(
412 res.begin(),
413 res.end(),
414 [&expr](auto ax_el)
415 {
416 return ax_el < expr.dimension();
417 }
418 ));
419
420 return res;
421 }
422
423 template <class R, class E, class C>
424 inline auto forward_normalize(E& expr, C&& axes) -> std::enable_if_t<
425 !xtl::is_signed<std::decay_t<decltype(*std::begin(axes))>>::value && !std::is_same<R, std::decay_t<C>>::value,
426 R>
427 {
428 static_cast<void>(expr);
429
430 R res;
431 xt::resize_container(res, std::size(axes));
432 std::copy(std::begin(axes), std::end(axes), std::begin(res));
433 XTENSOR_ASSERT(std::all_of(
434 res.begin(),
435 res.end(),
436 [&expr](auto ax_el)
437 {
438 return ax_el < expr.dimension();
439 }
440 ));
441 return res;
442 }
443
444 template <class R, class E, class C>
445 inline auto forward_normalize(E& expr, C&& axes) -> std::enable_if_t<
446 !xtl::is_signed<std::decay_t<decltype(*std::begin(axes))>>::value && std::is_same<R, std::decay_t<C>>::value,
447 R&&>
448 {
449 static_cast<void>(expr);
450 XTENSOR_ASSERT(std::all_of(
451 std::begin(axes),
452 std::end(axes),
453 [&expr](auto ax_el)
454 {
455 return ax_el < expr.dimension();
456 }
457 ));
458 return std::move(axes);
459 }
460
461 /******************
462 * get_value_type *
463 ******************/
464
465 template <class T>
467 {
468 using type = T;
469 };
470
471 template <class T>
472 requires requires { typename T::value_type; }
474 {
475 using type = typename T::value_type;
476 };
477
478 template <class T>
479 using get_value_type_t = typename get_value_type<T>::type;
480
481 /**********************
482 * get implementation *
483 **********************/
484
485 // When subclassing from std::tuple not all compilers are able to correctly instantiate get
486 // See here: https://stackoverflow.com/a/37188019/2528668
487 template <std::size_t I, template <typename... Args> class T, typename... Args>
488 decltype(auto) get(T<Args...>&& v)
489 {
490 return std::get<I>(static_cast<std::tuple<Args...>&&>(v));
491 }
492
493 template <std::size_t I, template <typename... Args> class T, typename... Args>
494 decltype(auto) get(T<Args...>& v)
495 {
496 return std::get<I>(static_cast<std::tuple<Args...>&>(v));
497 }
498
499 template <std::size_t I, template <typename... Args> class T, typename... Args>
500 decltype(auto) get(const T<Args...>& v)
501 {
502 return std::get<I>(static_cast<const std::tuple<Args...>&>(v));
503 }
504
505 /**************************
506 * to_array implementation *
507 ***************************/
508
509 namespace detail
510 {
511 template <class T, std::size_t N, std::size_t... I>
512 constexpr std::array<std::remove_cv_t<T>, N> to_array_impl(T (&a)[N], std::index_sequence<I...>)
513 {
514 return {{a[I]...}};
515 }
516 }
517
518 template <class T, std::size_t N>
519 constexpr std::array<std::remove_cv_t<T>, N> to_array(T (&a)[N])
520 {
521 return detail::to_array_impl(a, std::make_index_sequence<N>{});
522 }
523
524 /***********************************
525 * container_expression / data_interface_expression / strided_expression / iterable_expression *
526 ************************************************************************************************/
527
528 template <class T>
530
531 template <class T>
532 concept container_expression = requires {
534 requires !std::is_same_v<typename std::remove_cv<typename xcontainer_inner_types<T>::storage_type>::type, invalid_type>;
535 };
536
537 template <class T>
538 using get_storage_type_t = typename xcontainer_inner_types<T>::storage_type;
539
540 template <class E>
541 concept data_interface_expression = requires { std::declval<E>().data(); };
542
543 template <class E>
544 concept strided_expression = requires { std::declval<E>().strides(); };
545
546 template <class E>
547 concept iterable_expression = requires { std::declval<E>().begin(); };
548
549 /*************************
550 * conditional type cast *
551 *************************/
552
553 template <bool condition, class T>
555
556 template <class T>
557 struct conditional_cast_functor<false, T> : public xtl::identity
558 {
559 };
560
561 template <class T>
563 {
564 template <class U>
565 inline auto operator()(U&& u) const
566 {
567 return static_cast<T>(std::forward<U>(u));
568 }
569 };
570
579 template <bool condition, class T, class U>
580 inline auto conditional_cast(U&& u)
581 {
582 return conditional_cast_functor<condition, T>()(std::forward<U>(u));
583 }
584
585 /**********************
586 * tracking allocator *
587 **********************/
588
589 namespace alloc_tracking
590 {
591 inline bool& enabled()
592 {
593 static bool enabled;
594 return enabled;
595 }
596
597 inline void enable()
598 {
599 enabled() = true;
600 }
601
602 inline void disable()
603 {
604 enabled() = false;
605 }
606
607 enum policy
608 {
609 print,
610 assert
611 };
612 }
613
614 template <class T, class A, alloc_tracking::policy P>
615 struct tracking_allocator : private A
616 {
617 using base_type = A;
618 using value_type = typename A::value_type;
619 using reference = value_type&;
620 using const_reference = const value_type&;
621 using pointer = typename std::allocator_traits<A>::pointer;
622 using const_pointer = typename std::allocator_traits<A>::const_pointer;
623 using size_type = typename std::allocator_traits<A>::size_type;
624 using difference_type = typename std::allocator_traits<A>::difference_type;
625
626 tracking_allocator() = default;
627
628 T* allocate(std::size_t n)
629 {
630 if (alloc_tracking::enabled())
631 {
632 if (P == alloc_tracking::print)
633 {
634 std::cout << "xtensor allocating: " << n << "" << std::endl;
635 }
636 else if (P == alloc_tracking::assert)
637 {
638 XTENSOR_THROW(
639 std::runtime_error,
640 "xtensor allocation of " + std::to_string(n) + " elements detected"
641 );
642 }
643 }
644 return base_type::allocate(n);
645 }
646
647 using base_type::deallocate;
648
649// Construct and destroy are removed in --std=c++-20
650#if ((defined(__cplusplus) && __cplusplus < 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG < 202002L))
651 using base_type::construct;
652 using base_type::destroy;
653#endif
654
655 template <class U>
656 struct rebind
657 {
658 using traits = std::allocator_traits<A>;
659 using other = tracking_allocator<U, typename traits::template rebind_alloc<U>, P>;
660 };
661 };
662
663 template <class T, class AT, alloc_tracking::policy PT, class U, class AU, alloc_tracking::policy PU>
664 inline bool operator==(const tracking_allocator<T, AT, PT>&, const tracking_allocator<U, AU, PU>&)
665 {
666 return std::is_same<AT, AU>::value;
667 }
668
669 template <class T, class AT, alloc_tracking::policy PT, class U, class AU, alloc_tracking::policy PU>
670 inline bool operator!=(const tracking_allocator<T, AT, PT>& a, const tracking_allocator<U, AU, PU>& b)
671 {
672 return !(a == b);
673 }
674
675 /*****************
676 * assignable_to *
677 ******************/
678
679 template <class E1, class E2>
680 concept assignable_to_expression = requires { std::declval<const E2&>().assign_to(std::declval<E1&>()); };
681
682 /*************************************
683 * overlapping_memory_checker_traits *
684 *************************************/
685
686 template <class T>
687 concept addressable_to_expression = requires { std::addressof(*std::declval<T>().begin()); };
688
689 struct memory_range
690 {
691 // Checking pointer overlap is more correct in integer values,
692 // for more explanation check https://devblogs.microsoft.com/oldnewthing/20170927-00/?p=97095
693 const uintptr_t m_first = 0;
694 const uintptr_t m_last = 0;
695
696 explicit memory_range() = default;
697
698 template <class T>
699 explicit memory_range(T* first, T* last)
700 : m_first(reinterpret_cast<uintptr_t>(last < first ? last : first))
701 , m_last(reinterpret_cast<uintptr_t>(last < first ? first : last))
702 {
703 }
704
705 template <class T>
706 bool overlaps(T* first, T* last) const
707 {
708 if (first <= last)
709 {
710 return reinterpret_cast<uintptr_t>(first) <= m_last
711 && reinterpret_cast<uintptr_t>(last) >= m_first;
712 }
713 else
714 {
715 return reinterpret_cast<uintptr_t>(last) <= m_last
716 && reinterpret_cast<uintptr_t>(first) >= m_first;
717 }
718 }
719 };
720
721 template <class E, class Enable = void>
723 {
724 static bool check_overlap(const E&, const memory_range&)
725 {
726 return true;
727 }
728 };
729
730 template <class E>
731 struct overlapping_memory_checker_traits<E, std::enable_if_t<addressable_to_expression<E>>>
732 {
733 static bool check_overlap(const E& expr, const memory_range& dst_range)
734 {
735 if (expr.size() == 0)
736 {
737 return false;
738 }
739 else
740 {
741 return dst_range.overlaps(std::addressof(*expr.begin()), std::addressof(*expr.rbegin()));
742 }
743 }
744 };
745
746 struct overlapping_memory_checker_base
747 {
748 memory_range m_dst_range;
749
750 explicit overlapping_memory_checker_base() = default;
751
752 explicit overlapping_memory_checker_base(memory_range dst_memory_range)
753 : m_dst_range(std::move(dst_memory_range))
754 {
755 }
756
757 template <class E>
758 bool check_overlap(const E& expr) const
759 {
760 if (!m_dst_range.m_first || !m_dst_range.m_last)
761 {
762 return false;
763 }
764 else
765 {
766 return overlapping_memory_checker_traits<E>::check_overlap(expr, m_dst_range);
767 }
768 }
769 };
770
771 template <class Dst, class Enable = void>
772 struct overlapping_memory_checker : overlapping_memory_checker_base
773 {
774 explicit overlapping_memory_checker(const Dst&)
775 : overlapping_memory_checker_base()
776 {
777 }
778 };
779
780 template <class Dst>
781 struct overlapping_memory_checker<Dst, std::enable_if_t<addressable_to_expression<Dst>>>
782 : overlapping_memory_checker_base
783 {
784 explicit overlapping_memory_checker(const Dst& aDst)
785 : overlapping_memory_checker_base(
786 [&]()
787 {
788 if (aDst.size() == 0)
789 {
790 return memory_range();
791 }
792 else
793 {
794 return memory_range(std::addressof(*aDst.begin()), std::addressof(*aDst.rbegin()));
795 }
796 }()
797 )
798 {
799 }
800 };
801
802 template <class Dst>
803 auto make_overlapping_memory_checker(const Dst& a_dst)
804 {
806 }
807
808 /********************
809 * rebind_container *
810 ********************/
811
812 template <class X, template <class, class> class C, class T, class A>
813 struct rebind_container<X, C<T, A>>
814 {
815 using traits = std::allocator_traits<A>;
816 using allocator = typename traits::template rebind_alloc<X>;
817 using type = C<X, allocator>;
818 };
819
820// Workaround for rebind_container problems when C++17 feature is enabled
821#ifdef __cpp_template_template_args
822 template <class X, class T, std::size_t N>
823 struct rebind_container<X, std::array<T, N>>
824 {
825 using type = std::array<X, N>;
826 };
827#else
828 template <class X, template <class, std::size_t> class C, class T, std::size_t N>
829 struct rebind_container<X, C<T, N>>
830 {
831 using type = C<X, N>;
832 };
833#endif
834
835 /***************
836 * get_strides *
837 ***************/
838
839 template <class CP, class O, class A>
840 class xbuffer_adaptor;
841
842 namespace detail
843 {
844 template <class>
845 inline constexpr bool is_fixed_shape_v = false;
846
847 template <std::size_t... I>
848 inline constexpr bool is_fixed_shape_v<fixed_shape<I...>> = true;
849
850 template <class>
851 inline constexpr bool is_xbuffer_adaptor_v = false;
852
853 template <class CP, class O, class A>
854 inline constexpr bool is_xbuffer_adaptor_v<xbuffer_adaptor<CP, O, A>> = true;
855
856 template <class S>
857 concept fixed_shape_type = is_fixed_shape_v<S>;
858
859 template <class S>
860 concept xbuffer_adaptor_type = is_xbuffer_adaptor_v<S>;
861 }
862
863 // Defers strides-type mapping so callers can use it inside std::conditional_t<cond, A, B>::type
864 // without evaluating both branches (see xshared_expression in xexpression.hpp).
865 template <class S>
867 {
868 using type = typename rebind_container<std::ptrdiff_t, S>::type;
869 };
870
871 template <detail::fixed_shape_type S>
873 {
874 // TODO we could compute the strides statically here.
875 // But we'll need full constexpr support to have a
876 // homogenous ``compute_strides`` method
877 using type = std::array<std::ptrdiff_t, S::size()>;
878 };
879
880 template <detail::xbuffer_adaptor_type S>
881 struct get_strides_type<S>
882 {
883 // In bindings this mapping is called by reshape_view with an inner shape of type
884 // xbuffer_adaptor.
885 // Since we cannot create a buffer adaptor holding data, we map it to an std::vector.
886 using type = std::vector<typename S::value_type, typename S::allocator_type>;
887 };
888
889 template <class S>
890 using get_strides_t = typename get_strides_type<S>::type;
891
892 /*******************
893 * inner_reference *
894 *******************/
895
896 template <class ST>
897 using inner_reference_t = std::conditional_t<
898 std::is_const<std::remove_reference_t<ST>>::value,
899 typename std::decay_t<ST>::const_reference,
900 typename std::decay_t<ST>::reference>;
901
902 /************
903 * get_rank *
904 ************/
905
906 // Define the requirement
907 template <typename T>
908 concept HasRank = requires {
909 T::rank; // Checks if T::rank exists as a type nested member
910 };
911
912 template <class E>
913 constexpr std::size_t has_rank()
914 {
915 return HasRank<E>;
916 }
917
918 template <class E>
919 constexpr std::size_t get_rank()
920 {
921 if constexpr (HasRank<std::decay_t<E>>)
922 {
923 return std::decay_t<E>::rank;
924 }
925 return SIZE_MAX;
926 }
927
928 template <class E>
929 constexpr std::size_t has_fixed_rank()
930 {
931 return get_rank<E>() != SIZE_MAX;
932 }
933}
934
935#endif
Fixed shape implementation for compile time defined arrays.
auto arg(E &&e) noexcept
Calculates the phase angle (in radians) elementwise for the complex numbers in e.
Definition xcomplex.hpp:221
standard mathematical functions for xexpressions
auto accumulate(F &&f, E &&e, EVS evaluation_strategy=EVS())
Accumulate and flatten array NOTE This function is not lazy!
auto conditional_cast(U &&u)
Perform a type cast when a condition is true.
Definition xutils.hpp:580