I'm currently looking for ideas to improve our vector library. In Boost::uBlas I stumbled on a reference to Blitz++, which provdies a very neat looking method for initializing small sized vectors (and matrices):
Array<float,2> A(3,3); A = 1, 2, 3,
4, 5, 6,
7, 8, 9;
I couldn't pin down an actual download link for the lib, but after some thought I came up with a way to implement something that supports the above syntax:
#include <iostream>
template <int N> class Tuple; template <int N, int M>
class TupleArgs {
public:
Tuple<N>* tuple;
Tuple<M>* uninitialized;
TupleArgs(Tuple<N>* t, Tuple<M>* u) : tuple(t), uninitialized(u) {}
};
template <int N>
class Tuple {
public:
double head;
Tuple<N-1> tail;
Tuple() : head(0) {}
TupleArgs<N, N-1> operator=(double a) {
head = a;
return TupleArgs<N, N-1>(this, &tail);
}
Tuple<N>& operator=(const Tuple<N>& other) {
head = other.head;
tail = other.tail;
return *this;
}
};
template <>
class Tuple<1> {
public:
Tuple<1>() : head(0) {}
Tuple<1>(double a) : head(a) {}
Tuple<1>& operator=(const Tuple<1>& other) {
head = other.head;
return *this;
}
double head;
};
template <int N, int M>
TupleArgs<N, M-1> operator,(TupleArgs<N, M>& a, double b) {
a.uninitialized->head = b;
return TupleArgs<N, M-1>(a.tuple, &a.uninitialized->tail);
}
template <int N>
Tuple<N>& operator,(TupleArgs<N,1>& a, double b) {
a.uninitialized->head = b;
return *a.tuple;
}
int _tmain(int argc, _TCHAR* argv[])
{
Tuple<3> triple;
triple = 4, 5, 6;
std::cout << triple.head << ' ' << triple.tail.head << ' ' << triple.tail.tail.head << ' ' << std::endl;
Tuple<9> matrix33;
matrix33 = 1, 2, 0,
4, 5, 3,
7, 8, 2;
return 0;
}
Unfortunately, while I've found not only a way to use this kind of initialization syntax but did so for arbitrary (fixed) size arrays, all this comes at a cost:
1. overriding the comma operator destroys it's usual behaviour of respecting (left to right) evaluation order: the expressions in the initializer list will be evaluated in arbitrary order
2. I had to provide an implicitely usable constructor taking a double value, and that allows the compiler to implicitely convert variables or expressions of type double to
Tuple
. If someone accidentally uses a
double
when he intended a
Tuple
, that mistake won't be detected, and may be hard to detect later. Effectively, the C++ type safety is compromised.
3. Since the comma operator has extremely low priority (lower than assignment), intializing an array with an expression requires enclosing it in parenthesis. Failure to do so can break the code in many unforseeable ways. And potentially the break will be silent (i. e. the compiler may not be able to point out the problem).
4. The Tuple assignment operator cannot be chained directly: the values of the above assignment expressions are indeed of the correct type (
Tuple
), but the only way to chain the assignment is by enclosing the entire assignment in brackets:
Tuple<3> triple1, triple2;
triple1 = triple2 = 1, 2, 3; triple1 = (triple2 = 1, 2, 3);
My question:
Is it possible to avoid most of the drawbacks outlined above? I realize that item 1 can't be helped, but I wonder about the other three...
P.S.:
1. I've delved in the boost libraries before, but given their contrived syntax I haven't (yet) found the heart to check out Boost::Spirit, which also uses this kind of initialization syntax. If someone knows Boost::Spirit well enough to understand the mechanics used there, maybe he can point me to the relevant articles/bits of documentation/whatever I'd need to know to improve the above.
2. As mentioned above, I've found the Blitz++ documentation. But I couldn't pinpoint a working download link for the actual ibrary. It seems that it's no longer supported, but I wonder if the original source code is still available somewhere?
P.P.S.: sorry for the long question, but I believe all this information is required to point out what I already know - and anyway this forum is called quick answers, not quick questions ;)