How to properly initialize a boost multi_array of objects? - c++

I have been surprised to find that boost::multi_array seems to allocate its initial elements differently from, say, std::vector. It does not seem to fill each element with a unique element (using its default value or default constructor). I'm having trouble finding more information about this.
Is there a way to make the multi_array fill itself with a unique object at each element?
For example, consider the following:
static int num = 0;
struct A {
int n;
A() : n((::num)++) {
std::cout << "A()" << std::endl;
}
virtual ~A() {}
void print() {
std::cout << "n=" << n << std::endl;
}
};
int main() {
std::cout << "vector:" << std::endl;
std::vector<A> v(3);
for (auto x : v) {
x.print();
}
std::cout << "multi:" << std::endl;
boost::multi_array<A, 2> m(boost::extents[2][2]);
for (auto x : m) {
for (auto y : x) {
y.print();
}
}
}
This results in the output:
vector:
A()
A()
A()
n=0
n=1
n=2
multi:
A()
n=3
n=3
n=3
n=3
Why is the constructor called only once for the multi_array? How can the multi_array be "filled out" with unique objects (using A's default constructor)?

To quickly fill the whole array do something like fill_n¹:
std::fill_n(a.data(), a.num_elements(), 0);
With boost multi_array you can use a view to your own memory buffer to get the same performance (std::uninitialized_copy is your friend). (actually, you could even map an array view on existing memory, and you want to keep the existing values).
I've written a comparative demo about this here: pointers to a class in dynamically allocated boost multi_array, not compiling
Live On Coliru
#include <boost/multi_array.hpp>
#include <type_traits>
#include <memory>
struct octreenode { int a; int b; };
class world {
public:
world(double x, double y, double z, int widtheast, int widthnorth, int height)
:
originx(x), originy(y), originz(z),
chunkseast(widtheast), chunksnorth(widthnorth), chunksup(height)
{
#define OPTION 4
#if OPTION == 1
static_assert(std::is_trivially_destructible<octreenode>::value, "assumption made");
//std::uninitialized_fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
std::fill_n(chunk.data(), chunk.num_elements(), octreenode {1, 72});
#elif OPTION == 2
for(auto a:chunk) for(auto b:a) for(auto&c:b) c = octreenode{1, 72};
#elif OPTION == 3
for (index cz = 0; cz < chunksnorth; ++cz) {
for (index cx = 0; cx < chunkseast; ++cx) {
for (index cy = 0; cy < chunksup; ++cy) {
chunk[cz][cx][cy] = octreenode{1, 72};
}
}
}
#elif OPTION == 4
static_assert(std::is_trivially_destructible<octreenode>::value, "assumption made");
for (index cz = 0; cz < chunksnorth; ++cz) {
for (index cx = 0; cx < chunkseast; ++cx) {
for (index cy = 0; cy < chunksup; ++cy) {
new (&chunk[cz][cx][cy]) octreenode{1, 72};
}
}
}
#endif
(void) originx, (void) originy, (void) originz, (void) chunksup, (void) chunkseast, (void) chunksnorth;
}
private:
double originx, originy, originz;
int chunkseast, chunksnorth, chunksup;
#if 1
typedef boost::multi_array<octreenode, 3> planetchunkarray; // a boost_multi for chunks
typedef planetchunkarray::index index;
planetchunkarray chunk{boost::extents[chunksnorth][chunkseast][chunksup]};
#else
static_assert(boost::is_trivially_destructible<octreenode>::value, "assumption made");
std::unique_ptr<octreenode[]> raw { new octreenode[chunksnorth*chunkseast*chunksup] };
typedef boost::multi_array_ref<octreenode, 3> planetchunkarray;
typedef planetchunkarray::index index;
planetchunkarray chunk{raw.get(), boost::extents[chunksnorth][chunkseast][chunksup]};
#endif
};
int main() {
world w(1,2,3,4,5,6);
}
The variant using multi_array_ref is an example of how to avoid copy-constructing the elements (it's akin to the optimization used by std::vector when it uses uninitialized memory for reserved but unused elements).
¹ Of course for unique values, use std::iota or std::generate
http://en.cppreference.com/w/cpp/algorithm/iota
http://en.cppreference.com/w/cpp/algorithm/generate_n

So on further study, I learned two things:
boost::multi_array uses the copy constructor to initialize objects into the container, not the default constructor.
The for (auto x : container) way of looping in C++11 seems (at least with clang++ 3.5) to loop over copies of the container elements, rather than iterators (or references).
Modifying the original question's example to demonstrate point 1.
Adding a copy constructor (and corresponding counter), and using auto& x for the object loops rather than auto x:
static int num = 0;
static int cpy = 0;
struct A {
int n;
int c;
A() : n((::num)++), c(0) {
std::cout << "A_def()" << std::endl;
}
A(const A& o) : n(0), c((::cpy)++) {
std::cout << "A_cpy()" << std::endl;
}
virtual ~A() {}
void print() {
std::cout << "n=" << n << ",c=" << c << std::endl;
}
};
int main() {
std::cout << "vector:" << std::endl;
std::vector<A> v(3);
for (auto& x : v) {
x.print();
}
std::cout << "multi:" << std::endl;
boost::multi_array<A, 2> m(boost::extents[2][2]);
for (auto x : m) {
for (auto& y : x) {
y.print();
}
}
}
Produces the output
vector:
A_def() // <- vector allocation starts
A_def()
A_def()
n=0,c=0 // <- vector printing starts, using "for (auto& x)"
n=1,c=0
n=2,c=0
multi:
A_def() // <- a temporary object for multi_array allocation
A_cpy() // <- multi_array allocation starts
A_cpy()
A_cpy()
A_cpy()
n=0,c=0 // <- multi_array prints starts, using "for (auto& y)"
n=0,c=1
n=0,c=2
n=0,c=3
Modifying the example above to demonstrate point 2.
Same class definition as above in this answer, but removing the auto& x from the object loops, and going back to using auto x as done in the original question.
std::cout << "vector:" << std::endl;
std::vector<A> v(3);
for (auto x : v) {
x.print();
}
std::cout << "multi:" << std::endl;
boost::multi_array<A, 2> m(boost::extents[2][2]);
for (auto x : m) {
for (auto y : x) {
y.print();
}
}
Produces output that shows the copy constructor gets called during the print loops, even for elements in the vector.
vector:
A_def() // <- vector allocation starts
A_def()
A_def()
A_cpy() // <- vector printing starts, using "for (auto x)"
n=0,c=0
A_cpy()
n=0,c=1
A_cpy()
n=0,c=2
multi:
A_def() // <- a temporary object for multi_array allocation
A_cpy() // <- multi_array allocation starts
A_cpy()
A_cpy()
A_cpy()
A_cpy() // <- multi_array printing starts, using "for (auto y)"
n=0,c=7
A_cpy()
n=0,c=8
A_cpy()
n=0,c=9
A_cpy()
n=0,c=10

Related

C++ - Merging two sorted vectors of different, unique types with a common attribute into a new sorted vector

There are two types, A and B. These types both have a common attribute, a key.
There are two vectors of type A and B. These vectors are sorted by key ascending. The vectors are unique in regards to their keys - ie if a key is present in A_vect it is guaranteed to not be present in B_vect and vice versa.
The goal is to take the keys in vector A_vect, construct new B types and put them into B_vect, such that B contains it's original keys and it's new keys. The new B_vect should also be sorted.
I have this code snippet so far:
#include <iostream>
#include <vector>
#include <algorithm>
struct A
{
A(int i)
{
key = i;
}
int key;
};
struct B
{
B(int i)
{
key = i;
}
B()
{
}
int key;
};
int main()
{
std::vector<A> a_diff_sorted_vect = {1, 3};
std::vector<B> b_sorted_vect = {2, 4};
std::transform
(
a_diff_sorted_vect.begin(), a_diff_sorted_vect.end(),
std::back_inserter(b_sorted_vect),
[](const A &a) -> B
{
B b;
b.key = a.key;
return b;
}
);
std::cout << "Printing b_sorted_vect" << "\n";
for(auto element : b_sorted_vect)
{
std::cout << std::to_string(element.key) << "\n";
}
std::cout << "Finished printing b_sorted_vect" << "\n";
/*
std::sort
(
b_sorted_vect.begin(),
b_sorted_vect.end(),
[](B lhs, B rhs)
{
return lhs.key < rhs.key;
}
);
std::cout << "Printing b_sorted_vect" << "\n";
for(auto element : b_sorted_vect)
{
std::cout << std::to_string(element.key) << "\n";
}
std::cout << "Finished printing b_sorted_vect" << "\n";
*/
return 0;
}
Output:
2
4
1
3
Desired output:
1
2
3
4
Ideally I would like to avoid the top-down std::sort at the end, as I think there might be a more efficient way to do that during construction and merging. The std::sort is my current solution.
The use of a third temporary vector might be necessary, which is fine.
This should be done with C++11, using boost if necessary.

Sort just one member of the classes in a vector, leaving the other members unchanged

There are tons of answers for sorting a vector of struct in regards to a member variable. That is easy with std::sort and a predicate function, comparing the structs member. Really easy.
But I have a different question. Assume that I have the following struct:
struct Test {
int a{};
int b{};
int toSort{};
};
and a vector of that struct, like for example:
std::vector<Test> tv{ {1,1,9},{2,2,8},{3,3,7},{4,4,6},{5,5,5} };
I do not want to sort the vectors elements, but only the values in the member variable. So the expected output should be equal to:
std::vector<Test> tvSorted{ {1,1,5},{2,2,6},{3,3,7},{4,4,8},{5,5,9} };
I wanted to have the solution to be somehow a generic solution. Then I came up with a (sorry for that) preprocessor-macro-solution. Please see the following example code:
#include <iostream>
#include <vector>
#include <algorithm>
struct Test {
int a{};
int b{};
int toSort{};
};
#define SortSpecial(vec,Struct,Member) \
do { \
std::vector<decltype(Struct::Member)> vt{}; \
std::transform(vec.begin(), vec.end(), std::back_inserter(vt), [](const Struct& s) {return s.Member; }); \
std::sort(vt.begin(), vt.end()); \
std::for_each(vec.begin(), vec.end(), [&vt, i = 0U](Struct & s) mutable {s.Member = vt[i++]; }); \
} while (false)
int main()
{
// Define a vector of struct Test
std::vector<Test> tv{ {1,1,9},{2,2,8},{3,3,7},{4,4,6},{5,5,5} };
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
// Call sort macro
SortSpecial(tv, Test, toSort);
std::cout << "\n\nSorted\n";
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
}
Since macros shouldn't be used in C++, here my questions:
1. Is a solution with the algorithm library possible?
2. Or can this be achieved via templates?
To translate your current solution to a template solution is fairly straight forward.
template <typename T, typename ValueType>
void SpecialSort(std::vector<T>& vec, ValueType T::* mPtr) {
std::vector<ValueType> vt;
std::transform(vec.begin(), vec.end(), std::back_inserter(vt), [&](const T& s) {return s.*mPtr; });
std::sort(vt.begin(), vt.end());
std::for_each(vec.begin(), vec.end(), [&, i = 0U](T& s) mutable {s.*mPtr = vt[i++]; });
}
And we can call it by passing in the vector and a pointer-to-member.
SpecialSort(tv, &Test::toSort);
Somewhow like this (You just need to duplicate, rename and edit the "switchToShort" funtion for the rest of the variables if you want):
#include <iostream>
#include <vector>
struct Test {
int a{};
int b{};
int toSort{};
};
void switchToShort(Test &a, Test &b) {
if (a.toSort > b.toSort) {
int temp = a.toSort;
a.toSort = b.toSort;
b.toSort = temp;
}
}
//void switchToA(Test& a, Test& b) { ... }
//void switchToB(Test& a, Test& b) { ... }
inline void sortMemeberValues(std::vector<Test>& data, void (*funct)(Test&, Test&)) {
for (int i = 0; i < data.size(); i++) {
for (int j = i + 1; j < data.size(); j++) {
(*funct)(data[i], data[j]);
}
}
}
int main() {
std::vector<Test> tv { { 1, 1, 9 }, { 2, 2, 8 }, { 3,3 ,7 }, { 4, 4, 6 }, { 5, 5, 5} };
sortMemeberValues(tv, switchToShort);
//sortMemeberValues(tv, switchToA);
//sortMemeberValues(tv, switchToB);
for (const Test& t : tv) std::cout << t.a << " " << t.b << " " << t.toSort << "\n";
}
With range-v3 (and soon ranges in C++20), you might simply do:
auto r = tv | ranges::view::transform(&Test::toSort);
std::sort(r.begin(), r.end());
Demo

set_interaction example with custom vector not working

I'm looking to get intersection of two custom vectors - v and w - and then deleting common elements from original vector - v. But somehow size of y in my case is 0 always
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class A{
public:
int x;
A(int i) { x = i; }
~A() {}
void getx() { cout << x << " "; }
friend ostream &operator<<(ostream &o, const A&a) {
cout << a.x;
return o;
}
};
struct mycomparer {
bool operator()(const A* a, const A* b) {
return a->x < b->x;
}
};
int main()
{
vector<A*> v;
v.push_back(new A(1));
v.push_back(new A(2));
v.push_back(new A(4));
v.push_back(new A(3));
v.push_back(new A(5));
vector<A*> w;
w.push_back(new A(3));
w.push_back(new A(2));
vector<A*> y;
vector<A*>::iterator it, st;
it = set_intersection(v.begin(), v.end(), w.begin(), w.end(), y.begin(), mycomparer());
cout << " y size " << y.size() << endl;
if (y.size()) {
for (st = y.begin(); st != y.end(); st++)
v.erase(st);
}
for (st = v.begin(); st != v.end(); st++) {
printf("%d ", (*st)->x);
}
cout << endl;
return 0;
}
This is just a sample which I wrote and my intend is not to check for any other C++ rules.
You haven't obeyed the requirements of std::set_intersection.
Constructs a sorted range beginning at d_first consisting of elements that are found in both sorted ranges [first1, last1) and [first2, last2). If some element is found m times in [first1, last1) and n times in [first2, last2), the first std::min(m, n) elements will be copied from the first range to the destination range. The order of equivalent elements is preserved. The resulting range cannot overlap with either of the input ranges.
Neither v nor w are sorted w.r.t mycomparer. That's the first undefined behaviour.
Just passing y.begin() does not mean elements are added to y, for that you need std::back_inserter(y). That's the second undefined behaviour.
You haven't obeyed the requirements of std::vector::erase. It's argument is an iterator into that vector. You are using an iterator from y, not v. That's the third undefined behaviour.
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
class A{
public:
int x;
A(int i) { x = i; }
~A() {}
void getx() { cout << x << " "; }
friend ostream &operator<<(ostream &o, const A&a) {
cout << a.x;
return o;
}
};
struct mycomparer {
bool operator()(const A* a, const A* b) {
return a->x < b->x;
}
};
int main()
{
std::vector<A*> v;
v.push_back(new A(1));
v.push_back(new A(2));
v.push_back(new A(4));
v.push_back(new A(3));
v.push_back(new A(5));
std::sort(v.begin(), v.end(), mycomparer());
std::vector<A*> w;
w.push_back(new A(3));
w.push_back(new A(2));
std::sort(w.begin(), w.end(), mycomparer());
std::vector<A*> y;
set_intersection(v.begin(), v.end(), w.begin(), w.end(), std::back_inserter(y), mycomparer());
std::cout << " y size " << y.size() << std::endl;
for (st = y.begin(); st != y.end(); st++)
v.erase(std::find(v.begin(), v.end(), *st));
for (st = v.begin(); st != v.end(); st++) {
std::cout << (*st)->x) << std::endl;
}
return 0;
}
As an aside, you could instead use std::set_difference to find the elements in v not in w directly.
There are many issues with your code... I just put them in modified code
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
// don't using namespace std;
class A {
private:
int x; // don't expose internals
public:
constexpr A(int i) noexcept : x(i) {}
constexpr int GetX() const noexcept { return x; } // don't make a getter to print
friend std::ostream& operator<< (std::ostream& o, const A& a) {
o << a.x; // don't push to cout in an ostream operator <<
return o;
}
};
// use lambda instead of functors
static constexpr auto mycomparer =
[](std::shared_ptr<A> const& a, std::shared_ptr<A> const& b) noexcept {
return a->GetX() < b->GetX();
};
int main()
{
// you were not deleting the new elements: memory leak
std::vector<std::shared_ptr<A>> v; // e.g. use smart pointers
v.push_back(std::make_shared<A>(1));
v.push_back(std::make_shared<A>(2));
v.push_back(std::make_shared<A>(4));
v.push_back(std::make_shared<A>(3));
v.push_back(std::make_shared<A>(5));
std::vector<std::shared_ptr<A>> w;
w.push_back(std::make_shared<A>(3));
w.push_back(std::make_shared<A>(2));
std::vector<std::shared_ptr<A>> y;
//you have to sort before calling set_intersection
std::sort(std::begin(v), std::end(v), mycomparer);
std::sort(std::begin(w), std::end(w), mycomparer);
std::set_intersection(
std::cbegin(v), std::cend(v), // use const iterators
std::cbegin(w), std::cend(w),
std::back_inserter(y), mycomparer); // you cannot iterate over an empty array. Use the backinserter
std::cout << " y size " << y.size() << '\n';
// you cannot use an iterator to a vector y to delete from vector v!
//if (y.size() > 0) { // not required
for (auto const& el : y)
v.erase(std::find(std::cbegin(v), std::cend(v), el));
//}
for (auto const& el : v) {
std::cout << el->GetX() << " ";
}
std::cout << '\n'; // prefer \n over endl for speed
//return 0; // not required
}
1) In vector w you should push new A(2) first, then new A(3). For your actual code, your vectors must be sorted according to the comparator beforehand, as previously mentioned in commentaries, or you absolutely cannot use set_intersection.
2) vector y has size 0, because set_intersection does not insert new elements at iterator, but instead assigns with operator =. Instead of y.begin() you should use std::back_inserter(y), which would actually insert elements on assignment.

Detect initialized variables

Is there a way to check if certain variable is initialized before some point in a program?
For example, how to check if certain variable is initialized somewhere before the IfStmt node?
Methods from VarDecl class (hasInit() and getInit()) are not enough because of the following situation:
int x = 0; // hasInit() return true
int y;
...
y = 0; // initialized here, but hasInit() returns false
...
if (...) {}
If you maintain a product written by C++ code and hope to remove ugly indeterminate variables, a reasonable way to do it is defining an initializing function or lambda f, and then declare a local variable as const auto x = f(...); from the get-go.
OTOH, if you delay the value asignment on purpose, there are several methods to detect the value is assigned or not.
I just came up with following methods.
std::optional
In C++17 and over,
std::optional<T> enables us to detect whether values are assigned or not.
std::optional::has_value and std::optional::value correspond to your hasInit and getInit respectively as follows:
DEMO
#include <iostream>
#include <optional>
template<typename T>
void checkInitialization(const std::optional<T>& a)
{
if(a.has_value()){
std::cout << "Value is assigned by " << a.value() << "." << std::endl;
}
else{
std::cout << "Value is still not assigned." << std::endl;
}
}
int main(void)
{
std::optional<int> x;
checkInitialization(x); // Value is still not assigned
x = 1;
checkInitialization(x); // Value is assigned
return 0;
}
The output is as follows:
Value is still not assigned.
Value is assigned by 1.
std::unique_ptr
We can also check it using std::unique_ptr<T> which is introduced from C++11.
First we define a variable as std::unique_ptr<T> x; where (x == nullptr) is still true.
Later on, we assign a value by x = std::unique_ptr<int>(new int(1)) and then (x == nullptr) becomes false.
(In C++14 x = std::make_unique<int>(1) works and is simple.)
Thus we can again get the previous output with the following code:
DEMO
#include <iostream>
#include <memory>
template<typename T>
bool hasInit(const std::unique_ptr<T>& a)
{
return (a != nullptr);
}
template<typename T>
const T& getInit(const std::unique_ptr<T>& a)
{
return *a;
}
template<typename T>
void checkInitialization(const std::unique_ptr<T>& a)
{
if(hasInit(a)){
std::cout << "Value is assigned by " << getInit(a) << "." << std::endl;
}
else{
std::cout << "Value is still not assigned." << std::endl;
}
}
int main(void)
{
std::unique_ptr<int> x;
checkInitialization(x); // Uninitialized
x = std::unique_ptr<int>(new int(1));
//x = std::make_unique<int>(1); // C++14
checkInitialization(x); // Initialized
return 0;
}
std::pair
We can also apply std::pair<bool, T> where std::pair::first and std::pair::second correspond to your hasInit and getInit respectively.
We again get the previous output:
DEMO
#include <iostream>
#include <utility>
template<typename T>
void checkInitialization(const std::pair<bool, T>& a)
{
if(a.first){
std::cout << "Value is assigned by " << a.second << "." << std::endl;
}
else{
std::cout << "Value is still not assigned." << std::endl;
}
}
int main(void)
{
std::pair<bool, int> x{false, 0};
checkInitialization(x); // Uninitialized
x = {true, 1};
checkInitialization(x); // Initialized
return 0;
}
Firstly as mentioned in the comments:
int y = 0; // initialization
int y; y = 0; // assignment
Let's assume you want to detect assignment. One simple way could be wrap the integer you want to track in a struct and write a custom operator = (int). For example:
struct Foo
{
Foo() {std::cout << "default init" << std::endl;}
Foo& operator = (int elem)
{
cout<<"Int-Assignment operator called "<<endl;
x = elem;
is_assigned = true;
return *this;
}
int x = 0; // default initialized to 0
bool is_assigned = false; // default initialized to false
};
Now let's see what happens:
int main()
{
Foo t1;
// t1.is_assigned is false
t1 = 0;
// t1.is_assigned is true
return 0;
}
You could use something like this or a variant if needed. Here's the code running online corresponding to the above.
Is this what you wanted?

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
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:
let's say that the target class is a 3x3 matrix
any non-specified items in the initializer_list should be assumed to be zero.
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;
}
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;
}
};