Skip to content

Latest commit

 

History

History
268 lines (221 loc) · 6.75 KB

245.md

File metadata and controls

268 lines (221 loc) · 6.75 KB
Info

Example

template <class T, auto Dimensions> class mdarray2 {
public:
  template <class I1, class I2> constexpr T &operator[](I1 i1, I2 i2) {
    return vs_[i1][i2];
  }

private:
  std::array<std::array<T, 2>, Dimensions> vs_{};
};

int main() {
  mdarray2<int, 2> a{};
  a[1, 1] = 42;
  assert(0 == (a[0, 0]));
  assert(42 == (a[1, 1]));
}

https://gcc.godbolt.org/z/j9zh7YMz5

Puzzle

  • Can you implement a generic multidimensional array with multidimensional subscript operator?
template <class T, std::size_t N>
class mdarray; // TODO

int main() {
  using namespace boost::ut;

  "mdarray"_test = [] {
      "support multiple dimenions"_test = [] {
        mdarray<int, 2> a{2, 2};
        a[1, 1] = 42;
        expect(42_i == a[1, 1]);
      };

      "support multiple dimenions with different sizes"_test = [] {
        mdarray<int, 3> a{1, 3, 2};
        a[0, 1, 1] = 42;
        a[0, 2, 0] = 84;

        expect(0_i == a[0,0,0]);
        expect(0_i == a[0,2,1]);

        expect(42_i == a[0,1,1]);
        expect(84_i == a[0,2,0]);
      };
  };
}

https://gcc.godbolt.org/z/TP7reYK3b

Solutions

template <class T, std::size_t N>
class mdarray {
public:
    template<typename ... D>
    mdarray(D ... args) requires(sizeof...(D) ==N): dims{args...} {
        std::size_t storage = std::accumulate(dims.begin(), dims.end(), 1, std::multiplies<int>());
        vals.resize(storage);
    }

    template<typename ...D>
    T& operator[](D... args) requires(sizeof...(D) == N) {
        std::array<int, N> idx{args...};
        std::size_t offset = idx[0];
        for(std::size_t d=1; d<N; offset= offset*dims[d]+idx[d], d++);
        return vals[offset];
    }
private:
    std::array<int, N> dims;
    std::vector<T> vals;
};

https://gcc.godbolt.org/z/Ka7oo4r36

template <class T, std::size_t N>
class mdarray {
    std::array<std::size_t, N> dims_{};
    std::vector<T> data_{};

public:
    mdarray(const auto&... dims)
        : dims_{static_cast<std::size_t>(dims)...}
        , data_((dims * ...))
    {}

    template <class... Is>
    requires (sizeof...(Is) == N)
    [[nodiscard]] constexpr T& operator[](const Is&... is) {
        const std::array indexes{static_cast<std::size_t>(is)...};

        const auto offset = ranges::accumulate(
            ranges::views::zip(dims_, indexes),
            std::size_t{0},
            [](const auto offset, const auto zipped) {
                const auto [dim, idx] = zipped;
                return offset * dim + idx;
            });

        return data_[offset];
    }
};

https://gcc.godbolt.org/z/n5hxGoabP

template <class T, std::size_t N>
class mdarray {
public:
  mdarray(const auto... es) requires (sizeof...(es) == N) {
    const auto extent = (1 * ... * es);
    storage.reset(new T[extent]);
    if constexpr (std::is_trivially_constructible_v<T>) {
      std::fill_n(storage.get(), extent, T{});
    }
    const auto a = std::array{es..., 1};
    std::inclusive_scan(std::crbegin(a), std::prev(std::crend(a)),
                        std::rbegin(extents),
                        std::multiplies{});
  }

  auto operator[](const auto... is) -> T& requires (sizeof...(is) == N) {
    const auto indices = std::array{is...};
    return storage[std::transform_reduce(std::cbegin(indices), std::cend(indices),
                                         std::cbegin(extents), 0)];
  }

private:
  std::array<std::size_t, N> extents{};
  std::unique_ptr<T[]> storage{};
};

https://gcc.godbolt.org/z/6zhWvfcsP

template <class T, std::size_t N>
class mdarray: public std::vector<T>
{
    using Base = std::vector<T>;
    public:
    mdarray( auto ... ns ) requires (sizeof...( ns) == N  )
    : dim{ std::size_t( ns) ... }
    {
        assert( ( ( ns >=0 ) && ...) );
        Base::resize( (ns * ... * 1) );
        for ( int i = 1; i <N ; ++i )
            dim[i] *= dim[i-1];
        for ( int i = N-1 ; i >0; --i )
            dim[i] = dim[i-1];
        dim[0] = 1;
    }
    T & operator [] (  auto ... ns  ) requires (sizeof...( ns) == N)
    {
        assert( ( ( ns >=0 ) && ...) );
        auto indexes = std::forward_as_tuple(ns...);
        auto index = [&]<typename TT, auto ... Is>( std::integer_sequence<TT, Is... > const & ) {
            return ((std::get<Is>(indexes) * dim[Is]) + ...);
        }( std::make_index_sequence<N>());
        return Base::operator[](index);
    }
    private:
    std::array<std::size_t, N > dim;
};

https://gcc.godbolt.org/z/s8zqWsM31

template <class T, std::size_t N>
class mdarray {
public:
    template<class ...Dim>
    mdarray(Dim && ...dim) requires (sizeof...(dim) == N) : data((dim * ...)) {}

    T & operator[](auto &&...n) requires (sizeof...(n) == N) {
        auto inc = [i = 1] mutable { return i++; };
        return data[(((n * inc()) + ...))];
    }

private:
    std::vector<T> data;
};

https://gcc.godbolt.org/z/6ha3Y9c47

template <class T, std::size_t N>
class mdarray {
    std::array<std::size_t, N> dimensions;
    std::vector<T> values;

public:
    constexpr mdarray(std::convertible_to<std::size_t> auto... dimensions)
    requires (sizeof...(dimensions) == N) :
        dimensions{static_cast<std::size_t>(dimensions)...},
        values((1 * ... * dimensions)) {};

    constexpr auto &operator[](std::convertible_to<std::size_t> auto... indices)
    requires (sizeof...(indices) == N) {
        return [=]<auto... Is>(std::index_sequence<Is...>) -> auto & {
            return values[(0 + ... + [=]<auto... Js>(std::index_sequence<Js...>, auto index) {
                return index * (1 * ... * dimensions[Js]);
            }(std::make_index_sequence<Is>{}, indices))];
        }(std::make_index_sequence<N>{});
    }
};

https://gcc.godbolt.org/z/YEqxbTPx5

template <class T, std::size_t N>
class mdarray{
    private:
        std::array<int, N> dims_;
        std::vector<T> data_;

        auto getSize(auto ... dims)
        {
            return (1 * ... * dims);
        }

        auto getIndex(auto ... locs)
        {
            std::array<int, N> locations{locs...};

            auto index = locations[0];
            for(int i =1; i <N; ++i)
            {
                index += locations[i] * dims_[i-1];
            }
            return index;
        }

    public:
        mdarray(auto ... dims)
            : dims_{dims...},
              data_(getSize(dims...))
        {
        }

        T &operator[](auto ... locs) {
            return data_[getIndex(locs...)];
        }
};

https://gcc.godbolt.org/z/MMrdYeYsf