2

I'm trying to initialize my Matrix class with std::initializer_lists. I know I can do it with std::index_sequence, but I don't know how to expand them in one statement. This is how I do it:

template<size_t rows, size_t cols>
class Matrix {
public:
    Matrix(std::initializer_list<std::initializer_list<float>> il)
            : Matrix(il,
                     std::make_index_sequence<rows>(),
                     std::make_index_sequence<cols>()) {}

private:
    template<size_t... RowIs, size_t... ColIs>
    Matrix(std::initializer_list<std::initializer_list<float>> il,
           std::index_sequence<RowIs...>,
           std::index_sequence<ColIs...>)
            : values_{
            {
                    il.begin()[RowIs].begin()[ColIs]...
            }...
    } {}

public:
    float values_[rows][cols] = {};
};

It fails on the second expansion with error Pack expansion does not contain any unexpanded parameter packs. Maybe I can somehow specify which parameter pack I want to expand? Hope for your help!

sshd
  • 49
  • 1
  • 4
  • `il.begin()[RowIs].begin()[ColIs]`. This doesn't look right. `il.begin()` returns a pointer to the `std::initializer_list`. It's not an array or part of an array so you can't just use the index operator on it. What exactly is that statement meant to do? – David G May 05 '22 at 15:05
  • @DavidG, `begin` method returns an iterator to the first element. I can use index operator, I already implemented a `Vector` class this way, but the `Vector` class has 1d array, but `Matrix` has 2d array, I don't know how to expand different index sequences correctly – sshd May 05 '22 at 15:33

2 Answers2

2

I think the problem comes from the fact that RowIs and ColIs are expanded at the same time, i.e. both always having the same values during the initialization: 0, 1, 2...

You can check here that your current output (after fixing the compiler error) would be something like [[1.1, 5.5, 9.9], [0, 0, 0], [0, 0, 0]] for the matrix below:

Matrix<3, 3> m{
    { 1.1, 2.2, 3.3 },
    { 4.4, 5.5, 6.6 },
    { 7.7, 8.8, 9.9 }
};

Because you only read fromil[0,0], il[1,1], and il[2,2] in order to set values_.


What you could do is to create a sequence with a flat index, from 0 to Rows*Cols - 1, and read every value from il with FlatIs/Cols and FlatIs%Cols:

[Demo]

#include <array>
#include <cstdint>
#include <fmt/ranges.h>
#include <initializer_list>
#include <utility>

template<std::size_t Rows, std::size_t Cols>
class Matrix {
public:
    Matrix(std::initializer_list<std::initializer_list<float>> il)
        : Matrix(il, std::make_index_sequence<Rows*Cols>())
    {}

private:
    template<std::size_t... FlatIs>
    Matrix(std::initializer_list<std::initializer_list<float>> il,
        std::index_sequence<FlatIs...>)
        : values_{il.begin()[FlatIs/Cols].begin()[FlatIs%Cols]...}
    {}

public:
    std::array<std::array<float, Cols>, Rows> values_;
};

int main() {
    Matrix<4, 3> m{
        { 1.1, 2.2, 3.3 },
        { 4.4, 5.5, 6.6 },
        { 7.7, 8.8, 9.9 },
        { 3.1, 4.1, 5.9 }
    };
    fmt::print("{}", m.values_);
}

// Outputs:
//
//   [[1.1, 2.2, 3.3], [4.4, 5.5, 6.6], [7.7, 8.8, 9.9], [3.1, 4.1, 5.9]]
rturrado
  • 7,699
  • 6
  • 42
  • 62
0

If you have access to C++20, you can have the constructor take a (const) reference to a 2-dimensional C-style array of rows x cols elements. Then you can expand on the rows only and let std::to_array do the rest for you:

#include <array>
#include <cstdint>

template <std::size_t rows, std::size_t cols>
class Matrix {
   public:
    Matrix(float const (&il)[rows][cols])
        : Matrix(il, std::make_index_sequence<rows>()) {}

   private:
    template <std::size_t... RowIs>
    Matrix(float const (&il)[rows][cols], std::index_sequence<RowIs...>)
        : values_{std::to_array(il[RowIs])...} {}

   public:
    std::array<std::array<float, cols>, rows> values_;
};

int main() {
    Matrix<2, 3> m({{1, 2, 3}, {4, 5, 6}});
}

If you don't have access to std::to_array, it's pretty easy to implement it yourself (outside of namespace std).

paolo
  • 2,345
  • 1
  • 3
  • 17