3
\$\begingroup\$

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);
\$\endgroup\$
  • \$\begingroup\$ Enter the proposed std::view. You might find it interesting :) \$\endgroup\$ – Morwenn Jan 27 '16 at 16:19
  • \$\begingroup\$ @Morwenn std::view seems static. multi_view is dynamic. You can specify the dimensions at runtime. \$\endgroup\$ – Lingxi Jan 27 '16 at 16:24
  • \$\begingroup\$ It's ony optionally static. If you don't specify the dimensions at compile time, you pass them to the constructor at runtime. \$\endgroup\$ – Morwenn Jan 27 '16 at 16:27
  • \$\begingroup\$ @Morwenn Guess I will have to read the proposal through. I don't know std::view yet. Thanks for the mention :) \$\endgroup\$ – Lingxi Jan 27 '16 at 16:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.