Re: [eigen] Taking advantage of C++11 features in Eigen

[ Thread Index | Date Index | More lists.tuxfamily.org/eigen Archives ]


Il giorno 12/feb/2014, alle ore 12:44, Christoph Hertzberg <chtz@xxxxxxxxxxxxxxxxxxxxxxxx> ha scritto:

> On 11.02.2014 20:54, Nicola Gigante wrote:
>> I’m recently starting to follow the Eigen project and I was wondering which is the current status
>> of C++11 features support inside the library. It seems the library is “compatible” with C++11,
>> meaning that it compiles and works properly with the new standard, but does not take
>> advantage of new features when available.
> 
> Generally, we want to keep Eigen C++03 compatible as much as possible. There are C++11 things implemented which don't jeopardize that, like move semantics.
> https://bitbucket.org/eigen/eigen/src/90f8b868e6e2/Eigen/src/Core/Matrix.h?at=default#cl-220
> 

Glad to see that move certain types already have move constructors. I should have looked better at it but the online doc is from the
stable branch...

That said, what I suggest is not to break C++03 compatibility, of course. Since I’m following the evolution of the C++ language from the
compilers world, especially from the LLVM/Clang community, I could tell you that experience of other projects show that is very possible to
implement an API that’s 100% C++03 compatible (without regressions of any kind) and still takes advantage of C++11 features when 
available. 

Following, I could try to better explain my opinion on the matter.

> Things like initializer lists might encourage people to rely on that and causing trouble when back-porting to C++03. We might allow things like that if the user explicitly includes something like
> <Eigen/CXX11/Core> -- we already had a similar discussion about that for the tensor module:
> http://listengine.tuxfamily.org/lists.tuxfamily.org/eigen/2013/11/msg00000..html

I don’t agree very much on that. Switch to C++11 is a well-thought choice for users. First of all, it’s opt-in from the compiler 
point of view. You have to explicitly tell the compiler to compile in C++11 mode. You just don’t “happen” to be using a C++11
feature without knowing it. Then when one decides to use C++11 in his project, it’s not only a matter of new syntactic sugar or better 
performance. It has a broad impact on the entire design of an application or library, especially on new projects. I think
a useful feature shouldn’t be avoided just because the user might have troubles converting _its own_ code to not use it anymore.
You can make the choice even more explicit if you want, by testing a EIGEN_USE_CPP11 symbol or something like that.

What other libraries are doing is to conditionally support such features if explicitly enabled and supported by the compiler, 
and silently drop them if missing. From the users point of view is clean, painless, and enables them to choose how to write their code.

> ** Addendum after reading the link from Jitse's mail: It appears initializer lists have much less power than our current CommaInitializer
> 
First of all, since you’re going to retain C++03 compatibility, the CommaInitializer isn’t going to “disappear” as initially requested 
in that thread. Thus, enabling a simplified but less powerful syntax if the user is compiling in C++11 mode isn’t going to hurt anyone.

However, I’m not quite sure you can’t get all the CommaInitializer features with the new syntax. What you want to use is not
just “initializer lists”, but all the new “generalized initialization syntax”, that allows you to initialize a temporary with a braced list
instead of an explicit call to the constructor, like:

std::pair<bool, string> func() { return {true, “hello”}; }

As you see, here the braced list does not have to contain elements of the same type. In fact, that expression is simply calling pair’s 
constructor, and without modifications to the pair class itself. Combining this with initializer lists and variadic templates, I’m quite sure one
could end up with an initialization scheme that completely match the features of CommaInitializer without too much maintenance burden or 
feature divergence, since I think it would end up being a thin wrapper on CommaInitializer anyway.

In concrete, take the example from the mail you cited:
m << (Matrix3f() << 1, 2, 3, 4, 5, 6, 7, 8, 9).finished(),
     MatrixXf::Zero(3,cols-3),
     MatrixXf::Zero(rows-3,3),
     MatrixXf::Identity(rows-3,cols-3);

I think you can end up with a syntax like this:
m = {Matrix3f() << {1,2,3,4,5,6,7,8,9}, // here it’s similar to classic CommaInitializer, but get rid of finished()
     MatrixXf::Zero(3,cols-3),
     MatrixXf::Zero(rows-3,3),
     MatrixXf::Identity(rows-3,cols-3)}

or even this:
m = {{{1,2,3},  // even more explicit about structure, and easier to read
         {4,5,6},
         {7,8,9}}, 
     MatrixXf::Zero(3,cols-3),
     MatrixXf::Zero(rows-3,3),
     MatrixXf::Identity(rows-3,cols-3)}


This is really much easier to read and to learn (that finished() call is just syntactic noise after all, one might think),
especially for someone coming from common languages in scientific computing world like matlab or python.
The new syntax has been introduced exactly for use cases like this.

As noted in the concept examples above the syntax can still be implemented on top of operator<< if you don’t
want confusion in constructors and assignment operators, and it would still be clearer.

If this can be achieved without breaking the meaning of current code, silently dropping if language support is missing,
and as a thin wrapper around CommaInitializer (making them equivalent and thus easy to maintain both),
would you reject it from being included in Eigen?


> I think most other features you suggested are at the moment a bit risky having still a lot of compilers around without or with incomplete C++11 support.
Every single feature can be guarded by a separate preprocessor symbol based on the compiler version in use, to guarantee compatibility.
Anyway, things are not so slow as you might expect. Recent versions of Clang and G++ have been feature complete for a while now (even to 
C++14, FWIW), ICC supports all features relevant to my proposal, and even VC++, which is known to be the most standard-unaware 
compiler, have rvalue-references, and recently, initializer lists and variadic templates.

> IMHO we could consider using features if they give (measurable) performance improvements, but for now I wouldn't do it "just" for cleaner code or minor compile time reductions. We'd have to guard it with #ifdefs anyhow to keep C++03 compatibility, essentially making the code less clean.
> 

Of course implementing a piece of code twice just for cleaning up code is worthless if old code must be retained anyway. I’m not suggesting
anything like that. What I’m suggesting is taking advantage of features that allows _the user_ to write cleaner code, or achieve better
performance, of course. I agree that performance oriented changes must come with measurements of their impact.

> I hope I did not entirely block you from contributing to Eigen. In the long run we definitely want more C++11 features in Eigen, but for now we should not risk C++03 compatibility.

I have other things that I would like to to with Eigen (if not _in_ Eigen), if only to take confidence with the code base and learn
from your work, so don’t worry ;)
Anyway, I hope to have provided enough points to show you that C++03 compatibility is not at risk at all.

If yours is not a firm stop sign, I would like to start working on some ideas and, as time goes, we can see if they're worth or not.

Besides everything, I’ve not seen replies about the use of the alignas() keyword to avoid alignment problems for vectorized
operations. What do you think about it?

> 
> Christoph

Bye,
Nicola




Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/