/*************************************************************************** * Copyright (c) 2016, Johan Mabille, Sylvain Corlay and Wolf Vollprecht * * * * Distributed under the terms of the BSD 3-Clause License. * * * * The full license is in the file LICENSE, distributed with this software. * ****************************************************************************/ #ifndef XTENSOR_DYNAMIC_VIEW_HPP #define XTENSOR_DYNAMIC_VIEW_HPP #include #include #include "xexpression.hpp" #include "xiterable.hpp" #include "xlayout.hpp" #include "xsemantic.hpp" #include "xstrided_view_base.hpp" namespace xt { template class xdynamic_view; template struct xcontainer_inner_types> { using xexpression_type = std::decay_t; using undecay_expression = CT; using reference = inner_reference_t; using const_reference = typename xexpression_type::const_reference; using size_type = typename xexpression_type::size_type; using shape_type = std::decay_t; using undecay_shape = S; using storage_getter = FST; using inner_storage_type = typename storage_getter::type; using temporary_type = xarray, xexpression_type::static_layout>; static constexpr layout_type layout = L; }; template struct xiterable_inner_types> { using inner_shape_type = S; using inner_strides_type = inner_shape_type; using inner_backstrides_type = inner_shape_type; #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ == 8 static constexpr auto random_instantiation_var_for_gcc8_data_iface = has_data_interface>::value; static constexpr auto random_instantiation_var_for_gcc8_has_strides = has_strides>::value; #endif // TODO: implement efficient stepper specific to the dynamic_view using const_stepper = xindexed_stepper, true>; using stepper = xindexed_stepper, false>; }; /**************************** * xdynamic_view extensions * ****************************/ namespace extension { template struct xdynamic_view_base_impl; template struct xdynamic_view_base_impl { using type = xtensor_empty_base; }; template struct xdynamic_view_base : xdynamic_view_base_impl, CT, S, L, FST> { }; template using xdynamic_view_base_t = typename xdynamic_view_base::type; } /***************** * xdynamic_view * *****************/ namespace detail { template class xfake_slice; } template > class xdynamic_view : public xview_semantic>, public xiterable>, public extension::xdynamic_view_base_t, private xstrided_view_base> { public: using self_type = xdynamic_view; using base_type = xstrided_view_base; using semantic_base = xview_semantic; using extension_base = extension::xdynamic_view_base_t; using expression_tag = typename extension_base::expression_tag; using xexpression_type = typename base_type::xexpression_type; using base_type::is_const; using value_type = typename base_type::value_type; using reference = typename base_type::reference; using const_reference = typename base_type::const_reference; using pointer = typename base_type::pointer; using const_pointer = typename base_type::const_pointer; using size_type = typename base_type::size_type; using difference_type = typename base_type::difference_type; using inner_storage_type = typename base_type::inner_storage_type; using storage_type = typename base_type::storage_type; using iterable_base = xiterable; using inner_shape_type = typename iterable_base::inner_shape_type; using inner_strides_type = typename base_type::inner_strides_type; using inner_backstrides_type = typename base_type::inner_backstrides_type; using shape_type = typename base_type::shape_type; using strides_type = typename base_type::strides_type; using backstrides_type = typename base_type::backstrides_type; using stepper = typename iterable_base::stepper; using const_stepper = typename iterable_base::const_stepper; using base_type::contiguous_layout; using base_type::static_layout; using temporary_type = typename xcontainer_inner_types::temporary_type; using base_index_type = xindex_type_t; using simd_value_type = typename base_type::simd_value_type; using bool_load_type = typename base_type::bool_load_type; using strides_vt = typename strides_type::value_type; using slice_type = xtl::variant, xkeep_slice, xdrop_slice>; using slice_vector_type = std::vector; template xdynamic_view( CTA&& e, SA&& shape, get_strides_t&& strides, std::size_t offset, layout_type layout, slice_vector_type&& slices, get_strides_t&& adj_strides ) noexcept; template self_type& operator=(const xexpression& e); template disable_xexpression& operator=(const E& e); using base_type::dimension; using base_type::is_contiguous; using base_type::layout; using base_type::shape; using base_type::size; // Explicitly deleting strides method to avoid compilers complaining // about not being able to call the strides method from xstrided_view_base // private base const inner_strides_type& strides() const noexcept = delete; reference operator()(); const_reference operator()() const; template reference operator()(Args... args); template const_reference operator()(Args... args) const; template reference unchecked(Args... args); template const_reference unchecked(Args... args) const; reference flat(size_type index); const_reference flat(size_type index) const; using base_type::operator[]; using base_type::at; using base_type::back; using base_type::front; using base_type::in_bounds; using base_type::periodic; template reference element(It first, It last); template const_reference element(It first, It last) const; size_type data_offset() const noexcept; // Explicitly deleting data methods so has_data_interface results // to false instead of having compilers complaining about not being // able to call the methods from the private base value_type* data() noexcept = delete; const value_type* data() const noexcept = delete; using base_type::broadcast_shape; using base_type::expression; using base_type::storage; template bool has_linear_assign(const O& str) const noexcept; template void fill(const T& value); template stepper stepper_begin(const ST& shape); template stepper stepper_end(const ST& shape, layout_type l); template const_stepper stepper_begin(const ST& shape) const; template const_stepper stepper_end(const ST& shape, layout_type l) const; using container_iterator = std:: conditional_t; using const_container_iterator = typename storage_type::const_iterator; template using rebind_t = xdynamic_view>; template rebind_t build_view(E&& e) const; private: using offset_type = typename base_type::offset_type; slice_vector_type m_slices; inner_strides_type m_adj_strides; container_iterator data_xbegin() noexcept; const_container_iterator data_xbegin() const noexcept; container_iterator data_xend(layout_type l, size_type offset) noexcept; const_container_iterator data_xend(layout_type l, size_type offset) const noexcept; template It data_xbegin_impl(It begin) const noexcept; template It data_xend_impl(It end, layout_type l, size_type offset) const noexcept; void assign_temporary_impl(temporary_type&& tmp); template offset_type adjust_offset(offset_type offset, T idx, Args... args) const noexcept; offset_type adjust_offset(offset_type offset) const noexcept; template offset_type adjust_offset_impl(offset_type offset, size_type idx_offset, T idx, Args... args) const noexcept; offset_type adjust_offset_impl(offset_type offset, size_type idx_offset) const noexcept; template offset_type adjust_element_offset(offset_type offset, It first, It last) const noexcept; template friend class xstepper; friend class xview_semantic; friend class xaccessible; friend class xconst_accessible; }; /************************** * xdynamic_view builders * **************************/ template using xdynamic_slice = xtl::variant< T, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange_adaptor, xrange, xstepped_range, xkeep_slice, xdrop_slice, xall_tag, xellipsis_tag, xnewaxis_tag>; using xdynamic_slice_vector = std::vector>; template auto dynamic_view(E&& e, const xdynamic_slice_vector& slices); /****************************** * xfake_slice implementation * ******************************/ namespace detail { template class xfake_slice : public xslice> { public: using size_type = T; using self_type = xfake_slice; xfake_slice() = default; size_type operator()(size_type /*i*/) const noexcept { return size_type(0); } size_type size() const noexcept { return size_type(1); } size_type step_size() const noexcept { return size_type(0); } size_type step_size(std::size_t /*i*/, std::size_t /*n*/ = 1) const noexcept { return size_type(0); } size_type revert_index(std::size_t i) const noexcept { return i; } bool contains(size_type /*i*/) const noexcept { return true; } bool operator==(const self_type& /*rhs*/) const noexcept { return true; } bool operator!=(const self_type& /*rhs*/) const noexcept { return false; } }; } /******************************** * xdynamic_view implementation * ********************************/ template template inline xdynamic_view::xdynamic_view( CTA&& e, SA&& shape, get_strides_t&& strides, std::size_t offset, layout_type layout, slice_vector_type&& slices, get_strides_t&& adj_strides ) noexcept : base_type(std::forward(e), std::forward(shape), std::move(strides), offset, layout) , m_slices(std::move(slices)) , m_adj_strides(std::move(adj_strides)) { } template template inline auto xdynamic_view::operator=(const xexpression& e) -> self_type& { return semantic_base::operator=(e); } template template inline auto xdynamic_view::operator=(const E& e) -> disable_xexpression& { std::fill(this->begin(), this->end(), e); return *this; } template inline auto xdynamic_view::operator()() -> reference { return base_type::storage()[data_offset()]; } template inline auto xdynamic_view::operator()() const -> const_reference { return base_type::storage()[data_offset()]; } template template inline auto xdynamic_view::operator()(Args... args) -> reference { XTENSOR_TRY(check_index(base_type::shape(), args...)); XTENSOR_CHECK_DIMENSION(base_type::shape(), args...); offset_type offset = base_type::compute_index(args...); offset = adjust_offset(offset, args...); return base_type::storage()[static_cast(offset)]; } template template inline auto xdynamic_view::operator()(Args... args) const -> const_reference { XTENSOR_TRY(check_index(base_type::shape(), args...)); XTENSOR_CHECK_DIMENSION(base_type::shape(), args...); offset_type offset = base_type::compute_index(args...); offset = adjust_offset(offset, args...); return base_type::storage()[static_cast(offset)]; } template template inline bool xdynamic_view::has_linear_assign(const O&) const noexcept { return false; } template template inline auto xdynamic_view::unchecked(Args... args) -> reference { offset_type offset = base_type::compute_unchecked_index(args...); offset = adjust_offset(args...); return base_type::storage()[static_cast(offset)]; } template template inline auto xdynamic_view::unchecked(Args... args) const -> const_reference { offset_type offset = base_type::compute_unchecked_index(args...); offset = adjust_offset(args...); return base_type::storage()[static_cast(offset)]; } template inline auto xdynamic_view::flat(size_type i) -> reference { return base_type::storage()[data_offset() + i]; } template inline auto xdynamic_view::flat(size_type i) const -> const_reference { return base_type::storage()[data_offset() + i]; } template template inline auto xdynamic_view::element(It first, It last) -> reference { XTENSOR_TRY(check_element_index(base_type::shape(), first, last)); offset_type offset = base_type::compute_element_index(first, last); offset = adjust_element_offset(offset, first, last); return base_type::storage()[static_cast(offset)]; } template template inline auto xdynamic_view::element(It first, It last) const -> const_reference { XTENSOR_TRY(check_element_index(base_type::shape(), first, last)); offset_type offset = base_type::compute_element_index(first, last); offset = adjust_element_offset(offset, first, last); return base_type::storage()[static_cast(offset)]; } template inline auto xdynamic_view::data_offset() const noexcept -> size_type { size_type offset = base_type::data_offset(); size_type sl_offset = xtl::visit( [](const auto& sl) { return sl(size_type(0)); }, m_slices[0] ); return offset + sl_offset * m_adj_strides[0]; } template template inline void xdynamic_view::fill(const T& value) { return std::fill(this->linear_begin(), this->linear_end(), value); } template template inline auto xdynamic_view::stepper_begin(const ST& shape) -> stepper { size_type offset = shape.size() - dimension(); return stepper(this, offset); } template template inline auto xdynamic_view::stepper_end(const ST& shape, layout_type /*l*/) -> stepper { size_type offset = shape.size() - dimension(); return stepper(this, offset, true); } template template inline auto xdynamic_view::stepper_begin(const ST& shape) const -> const_stepper { size_type offset = shape.size() - dimension(); return const_stepper(this, offset); } template template inline auto xdynamic_view::stepper_end(const ST& shape, layout_type /*l*/) const -> const_stepper { size_type offset = shape.size() - dimension(); return const_stepper(this, offset, true); } template template inline auto xdynamic_view::build_view(E&& e) const -> rebind_t { inner_shape_type sh(this->shape()); inner_strides_type str(base_type::strides()); slice_vector_type svt(m_slices); inner_strides_type adj_str(m_adj_strides); return rebind_t( std::forward(e), std::move(sh), std::move(str), base_type::data_offset(), this->layout(), std::move(svt), std::move(adj_str) ); } template inline auto xdynamic_view::data_xbegin() noexcept -> container_iterator { return data_xbegin_impl(this->storage().begin()); } template inline auto xdynamic_view::data_xbegin() const noexcept -> const_container_iterator { return data_xbegin_impl(this->storage().cbegin()); } template inline auto xdynamic_view::data_xend(layout_type l, size_type offset) noexcept -> container_iterator { return data_xend_impl(this->storage().begin(), l, offset); } template inline auto xdynamic_view::data_xend(layout_type l, size_type offset) const noexcept -> const_container_iterator { return data_xend_impl(this->storage().cbegin(), l, offset); } template template inline It xdynamic_view::data_xbegin_impl(It begin) const noexcept { return begin + static_cast(data_offset()); } // TODO: fix the data_xend implementation and assign_temporary_impl template template inline It xdynamic_view::data_xend_impl(It begin, layout_type l, size_type offset) const noexcept { return strided_data_end(*this, begin + std::ptrdiff_t(data_offset()), l, offset); } template inline void xdynamic_view::assign_temporary_impl(temporary_type&& tmp) { std::copy(tmp.cbegin(), tmp.cend(), this->begin()); } template template inline auto xdynamic_view::adjust_offset(offset_type offset, T idx, Args... args) const noexcept -> offset_type { constexpr size_type nb_args = sizeof...(Args) + 1; size_type dim = base_type::dimension(); offset_type res = nb_args > dim ? adjust_offset(offset, args...) : adjust_offset_impl(offset, dim - nb_args, idx, args...); return res; } template inline auto xdynamic_view::adjust_offset(offset_type offset) const noexcept -> offset_type { return offset; } template template inline auto xdynamic_view::adjust_offset_impl(offset_type offset, size_type idx_offset, T idx, Args... args) const noexcept -> offset_type { offset_type sl_offset = xtl::visit( [idx](const auto& sl) { using type = typename std::decay_t::size_type; return sl(type(idx)); }, m_slices[idx_offset] ); offset_type res = offset + sl_offset * m_adj_strides[idx_offset]; return adjust_offset_impl(res, idx_offset + 1, args...); } template inline auto xdynamic_view::adjust_offset_impl(offset_type offset, size_type) const noexcept -> offset_type { return offset; } template template inline auto xdynamic_view::adjust_element_offset(offset_type offset, It first, It last) const noexcept -> offset_type { auto dst = std::distance(first, last); offset_type dim = static_cast(dimension()); offset_type loop_offset = dst < dim ? dim - dst : offset_type(0); offset_type idx_offset = dim < dst ? dst - dim : offset_type(0); offset_type res = offset; for (offset_type i = loop_offset; i < dim; ++i, ++first) { offset_type j = static_cast(first[idx_offset]); offset_type sl_offset = xtl::visit( [j](const auto& sl) { return static_cast(sl(j)); }, m_slices[static_cast(i)] ); res += sl_offset * m_adj_strides[static_cast(i)]; } return res; } /***************************************** * xdynamic_view builders implementation * *****************************************/ namespace detail { template struct adj_strides_policy { using slice_vector = V; using strides_type = dynamic_shape; slice_vector new_slices; strides_type new_adj_strides; protected: inline void resize(std::size_t size) { new_slices.resize(size); new_adj_strides.resize(size); } inline void set_fake_slice(std::size_t idx) { new_slices[idx] = xfake_slice(); new_adj_strides[idx] = std::ptrdiff_t(0); } template bool fill_args( const xdynamic_slice_vector& slices, std::size_t sl_idx, std::size_t i, std::size_t old_shape, const ST& old_stride, S& shape, get_strides_t& strides ) { return fill_args_impl>( slices, sl_idx, i, old_shape, old_stride, shape, strides ) || fill_args_impl>( slices, sl_idx, i, old_shape, old_stride, shape, strides ); } template bool fill_args_impl( const xdynamic_slice_vector& slices, std::size_t sl_idx, std::size_t i, std::size_t old_shape, const ST& old_stride, S& shape, get_strides_t& strides ) { auto* sl = xtl::get_if(&slices[sl_idx]); if (sl != nullptr) { new_slices[i] = *sl; auto& ns = xtl::get(new_slices[i]); ns.normalize(old_shape); shape[i] = static_cast(ns.size()); strides[i] = std::ptrdiff_t(0); new_adj_strides[i] = static_cast(old_stride); } return sl != nullptr; } }; } template inline auto dynamic_view(E&& e, const xdynamic_slice_vector& slices) { using view_type = xdynamic_view, dynamic_shape>; using slice_vector = typename view_type::slice_vector_type; using policy = detail::adj_strides_policy; detail::strided_view_args args; args.fill_args( e.shape(), detail::get_strides(e), detail::get_offset(e), e.layout(), slices ); return view_type( std::forward(e), std::move(args.new_shape), std::move(args.new_strides), args.new_offset, args.new_layout, std::move(args.new_slices), std::move(args.new_adj_strides) ); } } #endif