Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get linear index for multidimensional access

I'm trying to implement a multidimensional std::array, which hold a contigous array of memory of size Dim-n-1 * Dim-n-2 * ... * Dim-1. For that, i use private inheritance from std::array :

constexpr std::size_t factorise(std::size_t value)
{
    return value;
}

template<typename... Ts>
constexpr std::size_t factorise(std::size_t value, Ts... values)
{
    return value * factorise(values...);
}

template<typename T, std::size_t... Dims>
class multi_array : std::array<T, factorise(Dims...)>
{
    // using directive and some stuff here ...

    template<typename... Indexes>
    reference operator() (Indexes... indexes)
    {
        return base_type::operator[] (linearise(std::make_integer_sequence<Dims...>(), indexes...)); // Not legal, just to explain the need.
    }
 }

For instance, multi_array<5, 2, 8, 12> arr; arr(2, 1, 4, 3) = 12; will access to the linear index idx = 2*(5*2*8) + 1*(2*8) + 4*(8) + 3.

I suppose that i've to use std::integer_sequence, passing an integer sequence to the linearise function and the list of the indexes, but i don't know how to do it. What i want is something like :

template<template... Dims, std::size_t... Indexes>
auto linearise(std::integer_sequence<int, Dims...> dims, Indexes... indexes)
{
    return (index * multiply_but_last(dims)) + ...;
}

With multiply_but_last multiplying all remaining dimension but the last (i see how to implement with a constexpr variadic template function such as for factorise, but i don't understand if it is possible with std::integer_sequence).

I'm a novice in variadic template manipulation and std::integer_sequence and I think that I'm missing something. Is it possible to get the linear index computation without overhead (i.e. like if the operation has been hand-writtent) ?

Thanks you very much for your help.

like image 520
Nùménor Avatar asked Dec 02 '25 06:12

Nùménor


1 Answers

Following might help:

#include <array>
#include <cassert>
#include <iostream>

template <std::size_t, typename T> using alwaysT_t = T;

template<typename T, std::size_t ... Dims>
class MultiArray
{
public:
    const T& operator() (alwaysT_t<Dims, std::size_t>... indexes) const
    {
        return values[computeIndex(indexes...)];
    }
    T& operator() (alwaysT_t<Dims, std::size_t>... indexes)
    {
        return values[computeIndex(indexes...)];
    }

private:
    size_t computeIndex(alwaysT_t<Dims, std::size_t>... indexes_args) const
    {
        constexpr std::size_t dimensions[] = {Dims...};
        std::size_t indexes[] = {indexes_args...};

        size_t index = 0;
        size_t mul = 1;

        for (size_t i = 0; i != sizeof...(Dims); ++i) {
            assert(indexes[i] < dimensions[i]);
            index += indexes[i] * mul;
            mul *= dimensions[i];
        }
        assert(index < (Dims * ...));
        return index;
    }

private:
    std::array<T, (Dims * ...)> values;
};

Demo

I replaced your factorize by fold expression (C++17).

like image 157
Jarod42 Avatar answered Dec 03 '25 22:12

Jarod42



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!