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