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