Reed
Reed

Reputation: 75

How to determine size from (nested) std::initializer_list?

New to C++ and trying to wrap my head around initializer_list.

I'm making a Matrix class that effectively stores a 2d array of double values. I don't get the project on a structural level. Like okay we make a Matrix class that essentially stores a 2D array of data. But it needs to be able to store any size array, so it must use a dynamically allocated array. But std::array isn't allowed.

I have no idea how to access the items in the i_list. If they're passed in like

Matrix a = {{1, 2}, {3, 4}};

then according to the documentation I've seen, my only options for interaction with that information in the constructor are list.begin() which either points to the {1, 2} and list.end() which points to the {3,4}

std::vector and std::array are prohibited by the project description, and non-dynamic arrays obviously can't take in variables for size.

So how do I make this able to read a matrix of any size, and how do I take those values from my i_list and store them into something nondynamic?

I'm envisioning something like

Matrix::Matrix(const initializer_list & list) {
    double * mat[/*somehow find out size without dynamic allocation*/];
    for (double* i : mat) {
        *i = list[i]; //not how i_list works apparently
    }
}

Project description says: You MAY NOT use library classes such as std::array, std::vector, std::list, etc. for this project. You must implement your Matrix class internally using a dynamically allocated array

Upvotes: 4

Views: 1182

Answers (2)

EFenix
EFenix

Reputation: 831

Perhaps, you are looking for something like this:

struct Matrix {
  Matrix(std::initializer_list<std::initializer_list<double>> m) {
    int max=0;
    for (auto l: m)
      if (m.size()>max)
        max= m.size();
    std::cout << "your matriz seems to be: " 
              << m.size() << ' ' << max << std::endl;
  }
};

Upvotes: 0

Richard Hodges
Richard Hodges

Reputation: 69912

initializer_lists are very cheap containers of [references to] temporary objects.

You can iterate over them as if they were arrays. In addition they also have a size() member so you can query their size.

Here is an example of passing a '2d' initializer_list to a function (which could easily be an constructor):

#include <initializer_list>
#include <iostream>


using list_of_doubles = std::initializer_list<double>;
using list_of_list_of_doubles = std::initializer_list<list_of_doubles>;

void info(list_of_list_of_doubles lld)
{
    std::cout << "{\n";
    for (auto& ld : lld) {
        std::cout << "  {";
        auto sep = " ";
        for (auto& d : ld) {
            std::cout << sep << d;
            sep = ", ";
        }
        std::cout << " }\n";
    }

    std::cout << "}\n";
}

int main()
{
    info({
        { 1,2,3 },
        { 4.0, 5.0, 6.0 }
    });
}

expected output:

{
  { 1, 2, 3 }
  { 4, 5, 6 }
}

Printing out the contents of the list is pretty simple, but what if I want to save them non-dynamically? I'm making a class constructor, and I want to have access to that data.

OK, so the requirement is that the storage in the class is non-dynamic (i.e. a fixed size).

I am going to make some assumptions:

  1. let's say that the target class is a 3x3 matrix
  2. any non-specified items in the initializer_list should be assumed to be zero.
  3. passing in more than 3 rows or columns is a logic error and should cause an exception to be raised

Here's one (of many) ways:

#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <algorithm>


using list_of_doubles = std::initializer_list<double>;
using list_of_list_of_doubles = std::initializer_list<list_of_doubles>;

struct matrix
{
    matrix(list_of_list_of_doubles lld)
    : _storage {}
    {
        if (lld.size() > 3)
            throw std::invalid_argument("too many rows");
        auto row_idx = std::size_t { 0 };
        for (auto& row : lld) {
            if (row.size() > 3)
                throw std::invalid_argument("too many columns");
            std::copy(std::begin(row), std::end(row), std::begin(_storage[row_idx]));
            ++row_idx;
        }
    }

    double _storage[3][3];
};

std::ostream& operator<<(std::ostream& os, const matrix& m)
{
    std::cout << "{\n";
    for (auto& ld : m._storage) {
        std::cout << "  {";
        auto sep = " ";
        for (auto& d : ld) {
            std::cout << sep << d;
            sep = ", ";
        }
        std::cout << " }\n";
    }

    return std::cout << "}";
}

int main()
{
    matrix m({
        { 1,2,3 },
        { 4.1, 5.2, 6.3 },
        { 2.01, 4.5 }  // ,0
    });
    std::cout << m << std::endl;

}

but I wanted a dynamically-sized 2-d array...

Oh go on then...

#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>


using list_of_doubles = std::initializer_list<double>;
using list_of_list_of_doubles = std::initializer_list<list_of_doubles>;

std::size_t total_extent(const list_of_list_of_doubles& lld)
{
    return std::accumulate(std::begin(lld), std::end(lld), std::size_t(0),
                           [](auto tot, auto& container) {
                               return tot + container.size();
                           });

}

struct matrix
{
    using value_storage = std::unique_ptr<double[]>;
    using index_storage = std::unique_ptr<std::size_t>;

    matrix(list_of_list_of_doubles lld)
    : _total_extent { total_extent(lld) }
    , _rows { lld.size() }
    , _indecies { new std::size_t[_rows] }
    , _storage { new double [_total_extent] }
    {
        auto istorage = _storage.get();
        auto iindex = _indecies.get();
        for (auto& row : lld) {
            *iindex++ = istorage - _storage.get();
            istorage = std::copy(std::begin(row), std::end(row), istorage);
        }
    }

    std::size_t rows() const {
        return _rows;
    }

    const double* column(std::size_t row) const {
        return std::addressof(_storage[_indecies[row]]);
    }

    std::size_t column_size(std::size_t row) const {
        return row == _rows - 1
        ? _total_extent - _indecies[row]
        : _indecies[row + 1] - _indecies[row];
    }

    std::size_t _total_extent, _rows;
    std::unique_ptr<std::size_t[]> _indecies;
    std::unique_ptr<double[]> _storage;
};

std::ostream& operator<<(std::ostream& os, const matrix& m)
{
    std::cout << "{\n";
    for (std::size_t row = 0 ; row < m.rows() ; ++row) {
        std::cout << "  {";
        auto sep = " ";
        for (std::size_t col = 0 ; col < m.column_size(row) ; ++col) {
            std::cout << sep << m.column(row)[col];
            sep = ", ";
        }
        std::cout << " }\n";
    }

    return std::cout << "}";
}

int main()
{
    matrix m({
        { 1,2,3 },
        { 4.1, 5.2, 6.3 },
        { 2.01, 4.5 }  // ,0
    });
    std::cout << m << std::endl;

}

Upvotes: 3

Related Questions