Определите кортежи чисел в d измерениях и получите к ним доступ - PullRequest
0 голосов
/ 24 октября 2018

Я играю с идеей, которая может показаться искаженной, но мне нравится вызов, хотя я просто придумал это упражнение на C ++.

Задача состоит в том, чтобы хранить серию n скаляров в d измерениях.Представьте, что у вас есть вектор, матрица или трехмерная матрица из n , удваивается, или целое число, или что-то еще, и выбираете, сколько их во время выполнения (вычисляется вручную, ничего сложного).

Скажем, например, я бы хотел, чтобы массив 1D длиной 4 из двух двойных (n == 2, d == 1, во время выполнения я говорю, что хочу хранилище 4):

| d0 d1 | d0 d1 | d0 d1 | d0 d1 |

Или 2D 2x4 сетка из 3-х двойных (n == 3, d == 2, во время выполнения я говорю, что хочу хранилище 2 * 4):

| d0 d1 d2 | d0 d1 d2 | d0 d1 d2 | d0 d1 d2 |
| d0 d1 d2 | d0 d1 d2 | d0 d1 d2 | d0 d1 d2 |

Какнапример, я получаю доступ к i, j -ому элементу сетки?Конечно, с оператором ... но я хотел бы написать одиночный оператор , возвращающий кортеж ссылок трем типам двойных чисел.

Чтобы получитьидея:

template <std::size_t n, std::size_t d, typename Number>
class storage
{
public:

    // Magic tricks here?
    auto operator()(std::size_t i_0, ..., std::size_t i_d) -> std::tuple<n std::ref<Number>s>
    {

    }
};

Проблема в том, как я могу сыграть здесь некоторые магические трюки с шаблонами, чтобы определить один оператор, который возвращает кортеж однородных типов ?

Мне особенно трудно думать о том, как определить этот кортеж, и, конечно, об операторе, который должен иметь ровно d параметров.

Что касается использования, я 'я хотел бы иметь возможность работать следующим образом, например, в 2D-примере:

 // define a matrix of three doubles
 storage<3, 2, double> storageobj(8);

 // get a block, these should be references
 auto block = storageobj(0, 1);
 std::get<0>(block) = -123.456;

Обратите внимание, что фактическая реализация доступа к элементам в памяти сейчас не имеет для меня значения, но определениетипы возврата и как сделать operator() основной целью этого упражнения.

Может ли какая-то магическая ТМП помочь здесь?

1 Ответ

0 голосов
/ 24 октября 2018

Я изменил MultiArray с Как выделить и получить доступ к 3D, 4D, 5D массивам? , чтобы получить статический размер с std::array:

template <typename T, std::size_t Dim>
class MultiArray
{
public:

    explicit MultiArray(const std::array<std::size_t, Dim>& dimensions) :
        dimensions(dimensions),
        values(computeTotalSize(dimensions))
    {
        assert(!values.empty());
    }

    const T& get(const std::array<std::size_t, Dim>& indexes) const
    {
        return values[computeIndex(indexes)];
    }
    T& get(const std::array<std::size_t>& indexes)
    {
        return values[computeIndex(indexes)];
    }

    std::size_t computeIndex(const std::array<std::size_t, Dim>& indexes) const
    {
        size_t index = 0;
        size_t mul = 1;

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

    std::array<std::size_t, Dim> computeIndexes(std::size_t index) const
    {
        assert(index < values.size());

        std::array<std::size_t, Dim> res;

        std::size_t mul = values.size();
        for (std::size_t i = dimensions.size(); i != 0; --i) {
            mul /= dimensions[i - 1];
            res[i - 1] = index / mul;
            assert(res[i - 1] < dimensions[i - 1]);
            index -= res[i - 1] * mul;
        }
        return res;
    }

private:
    std::size_t computeTotalSize(const std::array<std::size_t, Dim>& dimensions) const
    {
        std::size_t totalSize = 1;

        for (auto i : dimensions) {
            totalSize *= i;
        }
        return totalSize;
    }

private:
    std::array<std::size_t, Dim> dimensions;
    std::vector<T> values;
};

Затем добавить слой вадаптируйте его к своему интерфейсу (возможно, MultiArray было написано аналогично storage_impl, чтобы избежать этого).

Чтобы преобразовать array<std::size_t, N> в std::size_t, .., std::size_t, мы используем std::index_sequence<0, 1, 2, .., N - 1>, чтобы разрешить расширение с вариациями.тогда мы просто должны преобразовать его в тип:

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

template <std::size_t n, typename Seq, typename Number>
class storage_impl;

template <std::size_t n, typename Number, std::size_t ... Is>
class storage_impl<n, std::index_sequence<Is...>, Number>
{
public:
    storage_impl(always_type<Is, std::size_t>... dims) : array{{{dims...}}} ()

    std::array<Number, n>&
    operator()(always_type<Is, std::size_t>... indexes)
    {
        return array.get({{indexes...}});
    }

    const std::array<Number, n>&
    operator()(always_type<Is, std::size_t>... indexes) const
    {
        return array.get({{indexes...}});
    }

private:
    MultiArray<std::array<Number, n>, sizeof...(Is)> array;
};

И наконец:

template <std::size_t n, std::size_t d, typename Number>
using storage = storage_impl<n, std::make_index_sequence<d>, Number>;

Использование будет похоже на:

// define a 2D matrix of three doubles
 storage<3, 2, double> storageobj(2, 4); // matrix 2x4 of std::array<double, 3>

 auto&& block = storageobj(0, 1); // std::array<double, 3>&
 std::get<0>(block) = -123.456;   // or block[0] = -123.456
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...