Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How could comma separated initialization such as in Eigen be possibly implemented in C++?

Here's a part of Eigen documentation:

Matrix3f m;
m << 1, 2, 3,
     4, 5, 6,
     7, 8, 9;
std::cout << m;

Output:

1 2 3
4 5 6
7 8 9

I couldn't understand how could all the comma separated values be captured by operator<< above. I did a tiny experiment:

cout << "Just commas: ";
cout << 1, 2, 3, 4, 5;
cout << endl;
cout << "Commas in parentheses: ";
cout << ( 1, 2, 3, 4, 5 );
cout << endl;

Predictably (according to my understanding of C++ syntax) only one of the values was captured by operator<< :

Just commas: 1
Commas in parentheses: 5

Thus the title question.

like image 737
Michael Avatar asked Apr 08 '15 18:04

Michael


2 Answers

The basic idea is to overload both the << and the , operators.

m << 1 is overloaded to put 1 into m and then returns a special proxy object – call it p – holding a reference to m.

Then p, 2 is overloaded to put 2 into m and return p, so that p, 2, 3 will first put 2 into m and then 3.

A similar technique is used with Boost.Assign, though they use += rather than <<.

like image 197
T.C. Avatar answered Sep 30 '22 05:09

T.C.


This is a possible simplified implementation

struct M3f {
    double m[3][3];
    struct Loader {
        M3f& m;
        int i;
        Loader(M3f& m, int i) : m(m), i(i) {}
        Loader operator , (double x) {
            m.m[i/3][i%3] = x;
            return Loader(m, i+1);
        }
    };
    Loader operator<<(double x) {
        m[0][0] = x;
        return Loader(*this, 1);
    }
};

The idea is that << returns a Loader instance that waits for second element, and each loader instance uses the comma operator to update the matrix and returns another loader instance.

Note that overloading the comma operator is generally considered a bad idea because the most specific characteristic of the operator is strict left-to-right evaluation order. However when overloaded this is not guaranteed and for example in

m << f(), g(), ...

g() could end up being called before f().

Note that I'm talking about the evaluation order, not about associativity or precedence that are of course maintained also for overloaded versions. For example g() could be called before f() but the result from f() is guaranteed to be correctly placed in the matrix before the result from g().

like image 24
6502 Avatar answered Sep 30 '22 05:09

6502