Sometimes, we create a one-dimensional object, but want to access it in a multi-dimensional way. For example, you can access an int[8] array object in a 2x2x2 way. So, I made a multi_view class template to make this easy. Any comments are welcome. Original code can be found here and documentation here.
template <typename Iterator>
class multi_view {
public:
static constexpr std::size_t max_dim = 10;
dimensions.
template <typename... Ts>
multi_view(Iterator base, Ts... extents)
: base_(base) {
constexpr auto n = sizeof...(extents);
static_assert(0 < n && n <= max_dim, "");
auto init_list = { extents... };
std::copy(init_list.begin(), init_list.end(), steps_);
auto i = n - 1;
std::size_t acc = 1;
do {
acc *= std::exchange(steps_[i], acc);
}
while (i-- > 0);
assert(acc);
num_elements_ = acc;
}
template <typename... Ts>
auto begin() const {
return base_;
}
template <typename... Ts>
auto begin(Ts... indices) const {
auto idxes = std::begin({ indices... });
std::size_t offset = 0;
for (std::size_t i = 0; i < sizeof...(indices) && steps_[i]; ++i) {
assert(idxes[i] >= 0);
offset += idxes[i] * steps_[i];
}
return (base_ + offset);
}
template <typename... Ts>
auto end() const {
return base_ + num_elements_;
}
template <typename... Ts>
auto end(Ts... indices) const {
return end_impl(std::make_index_sequence<sizeof...(Ts) - 1>{}, indices...);
}
template <typename... Ts>
auto operator ()(Ts... indices) const {
return *begin(indices...);
}
private:
template <std::size_t... seq, typename... Ts>
auto end_impl(std::index_sequence<seq...>, Ts... indices) const {
auto tup = std::tie(indices...);
return begin(std::get<seq>(tup)..., std::get<sizeof...(Ts) - 1>(tup) + 1);
}
Iterator base_;
std::size_t steps_[max_dim + 1]{};
std::size_t num_elements_ = 0;
};
template <typename Iterator, typename... Ts>
auto make_multi_view(Iterator base, Ts... extents) {
return multi_view<Iterator>(base, extents...);
}
A usage example:
std::vector<int> vec{ 1, 2, 3, 4 };
auto view2x2 = make_multi_view(vec.begin(), 2, 2);
assert(view2x2(0, 0), 1);
assert(view2x2(0, 1), 2);
assert(view2x2(1, 0), 3);
assert(view2x2(1, 1), 4);
assert(view2x2(), 1);
assert(view2x2(1), 3);
assert(std::distance(view2x2.begin(), view2x2.end()), 4);
assert(std::distance(view2x2.begin(1), view2x2.end(1), 2);
assert(std::distance(view2x2.begin(1, 1), view2x2.end(1, 1), 1);
std::view. You might find it interesting :) \$\endgroup\$std::viewseems static.multi_viewis dynamic. You can specify the dimensions at runtime. \$\endgroup\$std::viewyet. Thanks for the mention :) \$\endgroup\$std::mdspanmay be a good replacement for this code, if your underlying iterator is contiguous. BTW, where is the two-argumentassert()function defined? \$\endgroup\$