This bugzilla service is closed. All entries have been migrated to https://gitlab.com/libeigen/eigen

Bug 954

Summary: Support C++11 initializer lists
Product: Eigen Reporter: Ilja Honkonen <ilja.j.honkonen>
Component: Core - generalAssignee: Nobody <eigen.nobody>
Status: DECISIONNEEDED ---    
Severity: Feature Request CC: chtz, gael.guennebaud, ilja.j.honkonen, jacob.benoit.1
Priority: Lowest    
Version: 3.3 (current stable)   
Hardware: All   
OS: All   
Whiteboard:
Attachments:
Description Flags
program none

Description Ilja Honkonen 2015-02-13 16:32:56 UTC
Created attachment 544 [details]
program

The attached program assert even though I only use Eigen::Vector3d:

Assertion failed: (((SizeAtCompileTime == Dynamic && (MaxSizeAtCompileTime==Dynamic || size<=MaxSizeAtCompileTime)) || SizeAtCompileTime == size) && size>=0), function resize, file /Users/iljah/libraries/eigen/Eigen/src/Core/PlainObjectBase.h, line 282.

Program received signal SIGABRT, Aborted.
0x00007fff8f531866 in __pthread_kill ()
(gdb) up
#1  0x00007fff8c30635c in pthread_kill ()
(gdb) 
#2  0x00007fff8a6adb1a in abort ()
(gdb) 
#3  0x00007fff8a6779bf in __assert_rtn ()
(gdb) 
#4  0x0000000100000c87 in Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> >::resize (this=0x7fff5fbffa98, size=0) at PlainObjectBase.h:282
282	      eigen_assert(((SizeAtCompileTime == Dynamic && (MaxSizeAtCompileTime==Dynamic || size<=MaxSizeAtCompileTime)) || SizeAtCompileTime == size) && size>=0);
(gdb) 
#5  0x0000000100000c05 in Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 1, 0, 3, 1> >::_init1<int> (this=0x7fff5fbffa98, size=0) at PlainObjectBase.h:713
713	      resize(size);
(gdb) 
#6  0x0000000100000b9a in Eigen::Matrix<double, 3, 1, 0, 3, 1>::Matrix<int> (this=0x7fff5fbffa98, x=@0x7fff5fbffa74) at Matrix.h:244
244	      Base::template _init1<T>(x);
(gdb) 
#7  0x0000000100000b3d in Eigen::Matrix<double, 3, 1, 0, 3, 1>::Matrix<int> (this=0x7fff5fbffa98, x=@0x7fff5fbffa74) at Matrix.h:245
245	    }
(gdb) 
#8  0x0000000100000b16 in f<Eigen::Matrix<double, 3, 1, 0, 3, 1> > (data=@0x7fff5fbffac8) at test.cpp:7
7		return T{0};
(gdb) 
#9  0x0000000100000a9c in main () at test.cpp:14
14		const Eigen::Vector3d v = f(data);


Looks like the assert goes away if I use return T{0, 0, 0}; instead of return T{0}; This looks like a bug in initialization logic of Vector3d since that's what T is deduced to. I can't use {0, 0, 0} if T is double so supporting also scalar init of matrices would be nice. And looks like old style initialization with T(0) gives the same result.
Comment 1 Christoph Hertzberg 2015-02-13 16:45:20 UTC
You are calling Vector3d(0), which asserts as intended. What behavior would you expect?
If you want a vector of zeros, you must use T::Constant(0) or T::Zero().
Adding a single parameter constructor which initializes fixed-sized matrices by that scalar is likely to cause more trouble than it solves.
Comment 2 Ilja Honkonen 2015-02-13 19:16:25 UTC
> You are calling Vector3d(0), which asserts as intended. What behavior would
> you expect?

I'd expect http://en.cppreference.com/w/cpp/language/aggregate_initialization to work, specifically "If the number of initializer clauses is less than the number of members or initializer clauses is completely empty, the remaining members are initialized by"... 

> If you want a vector of zeros, you must use T::Constant(0) or T::Zero().

Which only works with Eigen as far as I know so I can't write a generic function that needs to initialize a template parameter to 0 without additional code for e.g. Eigen.

> Adding a single parameter constructor which initializes fixed-sized matrices
> by that scalar is likely to cause more trouble than it solves.

Doesn't seem to be the case for standard types and as you can see some people will be surprised by Eigen not working similarly even though it could.
Comment 3 Christoph Hertzberg 2015-02-13 21:26:19 UTC
(In reply to Ilja Honkonen from comment #2)
> > You are calling Vector3d(0), which asserts as intended. What behavior would
> > you expect?
> 
> I'd expect
> http://en.cppreference.com/w/cpp/language/aggregate_initialization to work,
> specifically "If the number of initializer clauses is less than the number
> of members or initializer clauses is completely empty, the remaining members
> are initialized by"... 

We don't have brace-or-equal initializers in Eigen. Could you just answer what values you expect Vector3d to have, if you initialize it, e.g., using
Vector3d{1,2}; Vector3d{2};
Shall the remaining elements be 0.0, undefined or equal to the first element?
std::array<double, 3> appears to initialize by 0 and I would not say that Vector3d{2}=={2,0,0} would be very intuitive.
Comment 4 Christoph Hertzberg 2015-02-13 21:29:26 UTC
I edited this bug to a feature request for C++11 initializer lists. I thought we had a request for that already, but maybe we just had a mailing list discussion.

I'm still not convinced about supporting Vector3d{1}, or Vector3d{1,2}. Especially, the behavior of std::array I don't find intuitive at all.
Comment 5 Ilja Honkonen 2015-02-13 22:47:57 UTC
> We don't have brace-or-equal initializers in Eigen. Could you just answer
> what values you expect Vector3d to have, if you initialize it, e.g., using
> Vector3d{1,2}; Vector3d{2};
> Shall the remaining elements be 0.0, undefined or equal to the first element?

I would go with standard behavior, so unspecified items would be value-initialized which for doubles means zero-initialization if I'm reading cppref correctly and init of std::array also works.

With this behavior I could use e.g. = T{0} + 2 to initialize both a double and a vector3d to 2 (, 2, 2, ...) assuming if eigen allowed + in addition to *...

> std::array<double, 3> appears to initialize by 0 and I would not say that
> Vector3d{2}=={2,0,0} would be very intuitive.

What about Vector3d{}? Not initializing is an option but (http://en.cppreference.com/w/cpp/language/default_initialization) "If T is an array type, every element of the array is default-initialized." and to me eigen feels like an array type. VectorNd{1} looks like an exception because for e.g. Vector4d{1,2} none of the other options [(1,2,2,2), (1,2,1,2), (1,2,1,1) or even (1,2,2,1)] are any easier to remember.

Standard behavior seems to be the only consistent way to initialize a vector when given no values {}, all values {a, b, c, ...} and everything in between.

For dynamic matrices I suggest emulating behavior of http://en.cppreference.com/w/cpp/numeric/valarray/valarray
Comment 6 Christoph Hertzberg 2015-02-14 16:28:52 UTC
(In reply to Ilja Honkonen from comment #5)
> I would go with standard behavior, so unspecified items would be
> value-initialized which for doubles means zero-initialization if I'm reading
> cppref correctly and init of std::array also works.

I think this would rather hide programming errors of accidentally passing to few parameters than being convenient.

> With this behavior I could use e.g. = T{0} + 2 to initialize both a double
> and a vector3d to 2 (, 2, 2, ...) assuming if eigen allowed + in addition to
> *...

Element-wise addition is supported for Eigen::Array, not for Eigen::Matrix -- we had several discussions about that which I'm not going to repeat here.

> What about Vector3d{}? Not initializing is an option but

Currently, as we don't have initializer-list constructors, it falls back to the default constructor and I think no initialization is the expected behavior for that.
You may consider compiling with EIGEN_INITIALIZE_MATRICES_BY_ZERO if you want (all) your matrices be initialized with 0.
If we implement initializer-list constructors, I could agree on making the empty list initialize to 0. to match std::array behavior. But other than that, IMO the only good option for non-matching number of parameters is to fail (if possible at compile time).

> For dynamic matrices I suggest emulating behavior of
> http://en.cppreference.com/w/cpp/numeric/valarray/valarray

For VectorXd{...} I agree -- that's merely an implementation issue. 
But what about MatrixXd{1,2,3,4}? Shall this be a 1x4 or a 4x1 vector?
Comment 7 Gael Guennebaud 2015-02-14 21:49:47 UTC
Ilja,

if you want something that is as compatible as possible with a Scalar, then you should rather use Eigen::Array for which Eigen::Array3d(M_PI) creates an array with all elements initialized to M_PI (need the devel branch).

As Christoph said, for linear-algebra objects as Eigen::Vector* or Eigen::Matrix<> this wont be supported because there is no natural behavior.



Christoph,

Agree to have an empty list initialize to 0.

Agree to fail if the number of parameters do not match (expect for Array initialized by a singleton).

For 2D objects (e.g., ArrayXXf or MatrixXd), we could request something like {{1,2},{3,4}}. The interpretation should depend on the storage layout, so a MatrixXd initialized with {{1,2},{3,4}} would create:

1 3
2 4

See also the Tensor module for which such a mechanism already exist (though it has to be adapted to follow the storage layout).
Comment 8 Christoph Hertzberg 2015-02-16 00:30:02 UTC
(In reply to Gael Guennebaud from comment #7)
> Agree to fail if the number of parameters do not match (expect for Array
> initialized by a singleton).

Ok, for Array it makes sense, since assigning a scalar to an Array is implemented, as well.

> For 2D objects (e.g., ArrayXXf or MatrixXd), we could request something like
> {{1,2},{3,4}}. The interpretation should depend on the storage layout, so a
> MatrixXd initialized with {{1,2},{3,4}} would create:
> 
> 1 3
> 2 4

That would be confusing compared to the comma-initializer, which is always row-major:
  MatrixXd A(2,2);
  A << 1,2,
       3,4;

> See also the Tensor module for which such a mechanism already exist (though
> it has to be adapted to follow the storage layout).

I'm tending to always make these kinds of initializers follow lexicographic order. And if one really wants different order, this shall be explicitly specified. Generally, I think storage order should only influence low-level functionality, such as .data() and Map<...> (and of course the internal behavior).
Comment 9 Ilja Honkonen 2015-02-20 17:44:58 UTC
> > I would go with standard behavior, so unspecified items would be
> > value-initialized which for doubles means zero-initialization if I'm reading
> > cppref correctly and init of std::array also works.
> I think this would rather hide programming errors of accidentally passing to
> few parameters than being convenient.
> > With this behavior I could use e.g. = T{0} + 2 to initialize both a double
> > and a vector3d to 2 (, 2, 2, ...) assuming if eigen allowed + in addition to
> > *...
> Element-wise addition is supported for Eigen::Array, not for Eigen::Matrix
> -- we had several discussions about that which I'm not going to repeat here.

I forgot about Array, I guess I can use it temporarily since it can be assigned to a Vector after the scalar operation.

> > What about Vector3d{}? Not initializing is an option but
> Currently, as we don't have initializer-list constructors, it falls back to
> the default constructor and I think no initialization is the expected
> behavior for that.
> You may consider compiling with EIGEN_INITIALIZE_MATRICES_BY_ZERO if you
> want (all) your matrices be initialized with 0.

A preprocessor directive doesn't seem like a good way to go. What about adding another template parameter for allocation/initialization policy? The default value could be what is done now to preserve backwards compatibility. With delegating constructors etc. it should also allow customization of constructor behavior. By changing the policy Eigen containers could leave value(s) uninitialized, etc.

> If we implement initializer-list constructors, I could agree on making the
> empty list initialize to 0. to match std::array behavior. But other than
> that, IMO the only good option for non-matching number of parameters is to
> fail (if possible at compile time).
> 
> > For dynamic matrices I suggest emulating behavior of
> > http://en.cppreference.com/w/cpp/numeric/valarray/valarray
> 
> For VectorXd{...} I agree -- that's merely an implementation issue. 
> But what about MatrixXd{1,2,3,4}? Shall this be a 1x4 or a 4x1 vector?

As MatrixXd is quite close to e.g. std::vector<std::vector<...>> it might make sense to require {{...}, {...}, ...} for initialization.
Comment 10 Ilja Honkonen 2015-02-20 17:52:58 UTC
> For 2D objects (e.g., ArrayXXf or MatrixXd), we could request something like
> {{1,2},{3,4}}. The interpretation should depend on the storage layout, so a
> MatrixXd initialized with {{1,2},{3,4}} would create:

An easier to remember approach would be to always have e.g. inner most {}s initialize the last or first index, etc. regardless of storage order. This could also be decided by the init policy.
Comment 11 Christoph Hertzberg 2015-02-20 19:18:21 UTC
(In reply to Ilja Honkonen from comment #9)
> > You may consider compiling with EIGEN_INITIALIZE_MATRICES_BY_ZERO if you
> > want (all) your matrices be initialized with 0.
> 
> A preprocessor directive doesn't seem like a good way to go. What about
> adding another template parameter for allocation/initialization policy?

While I admit that the preprocessor directive is only a hack, I don't really like adding a template parameter for that, either. Mostly, because it only influences the construction but might lead to different instantiations of equivalent code afterwards (cf Bug 416).

Btw, an easy solution for your special case would be to inherit from Vector3d (or Matrix<double, N, 1> with templated N) and implement your own custom constructors that fit your need:

template<int N>
MyVector : public Eigen::Matrix<double, N, 1> {
  MyVector(const double& value = 0.0) { this->setConstant(value); }
  MyVector(const std::initializer_list<double>&) {...};
};
Comment 12 Nobody 2019-12-04 14:13:46 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to gitlab.com's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.com/libeigen/eigen/issues/954.