
Dear All, This is my review of Emil Dotchevski's QVM library. Thank you to Emil for offering this code. I believe that Boost and/or the standard library would benefit from vectors and matrices. Unfortunately I don't think that this proposal is what is needed, and I feel it should not be accepted. I have previously posted my thoughts about the terseness of the identifiers used and the use of operator,; I won't repeat the details here but in summary I find the syntax not to my taste, and not suitable for "public" code. Boost should aim to promote best practice, and I think this library falls short. Now, it's not unusual to find libraries that have interfaces that we don't like. Often we'll use them anyway because the features that the libraries provide outweigh the pain of using them. Does this apply in the case of QVM? Actually since first looking I've found that it does have more functionality than I thought - for example, I've only just found that it has functions to create matrices representing rotations (but not any other affine transformations) and to compute the inverse of a matrix (but not the determinant). But there's still not enough meat here for me to think it's worth using. If this were a comprehensive matrix algebra library, or a computational geometry library, or a SIMD library, then it might be worth putting up with the syntax. But it isn't, it's just a medium-size collection of operators and functions for quaternions, vectors and matrices that many projects will already having amongst their own local utilities. What it does offer is the ability to wrap existing matrix and vector classes and use a single unified syntax for all of them. It's just a shame that the single unified syntax is not the one I want to use! It is interesting to contrast QVM with Boost.Polygon and Boost. Geometry. The similarity is that both of these also wrap existing classes (for e.g. points) by traits class specialisations. When these were reviewed we discussed extensively how support for this compromised the friendliness of the syntax, i.e. the need to write get<0>(p) rather than p.x. What we ended up with there was I think the best that we could do, and it was probably worthwhile because of the substantial core functionality of each of those libraries. In my opinion, QVM doesn't reach that threshold. To address some of the "standard questions": I've not looked at the code, only the docs; I've spent a couple of hours this time and also looked when it was first proposed; and yes I do use vectors and matrices in my own code, for various graphics and cartographic purposes. Regards, Phil.

Phil, thank you for your review. On Wed, Dec 16, 2015 at 12:26 PM, Phil Endecott < spam_from_boost_dev@chezphil.org> wrote:
Actually since first looking I've found that it does have more functionality than I thought - for example, I've only just found that it has functions to create matrices representing rotations (but not any other affine transformations)
Not true, it can do translation and scaling as well. There is no shear support right now but that's easy to add if needed. The expression trans_m(v) reinterprets v as a translation matrix. Scaling is done by diag_m(v), which reinterprets v as a matrix whose diagonal is the elements of v with all other elements zero.
and to compute the inverse of a matrix (but not the determinant).
Not true, there is the determinant() function, and there are actually two overloads of inverse, one that itself computes the determinant, and another that takes the determinant as an argument, in case it was computed already. Generally, the library's functionality covers 3D graphics pretty well. I am open to reasonable additions, as discussed earlier.
But there's still not enough meat here for me to think it's worth using. If this were a comprehensive matrix algebra library, or a computational geometry library, or a SIMD library, then it might be worth putting up with the syntax. But it isn't, it's just a medium-size collection of operators and functions for quaternions, vectors and matrices that many projects will already having amongst their own local utilities.
What you're saying is that people who need quaternions, vectors and matrices already have quaternions vectors and matrices. That is true, in fact there are very many such types, and the point of QVM isn't to add to the mix but to define an environment where all of the existing types can be used safely together.
What it does offer is the ability to wrap existing matrix and vector classes and use a single unified syntax for all of them. It's just a shame that the single unified syntax is not the one I want to use!
It is interesting to contrast QVM with Boost.Polygon and Boost. Geometry. The similarity is that both of these also wrap existing classes (for e.g. points) by traits class specialisations. When these were reviewed we discussed extensively how support for this compromised the friendliness of the syntax, i.e. the need to write get<0>(p) rather than p.x. What we ended up with there was I think the best that we could do, and it was probably worthwhile because of the substantial core functionality of each of those libraries. In my opinion, QVM doesn't reach that threshold.
The swizzling syntax must be terse or else it's useless. If you've written shader code you'll know utterly inadequate it is to require a syntax like swizzle<1,0,3,2>(vec) instead of (vec,YXWZ). At any rate, swizzling is defined in a separate header, don't include it if you don't want it. I've agreed that other function names can be made longer. Thanks, Emil

On 16.12.2015 22:21, Emil Dotchevski wrote:
The swizzling syntax must be terse or else it's useless. If you've written shader code you'll know utterly inadequate it is to require a syntax like swizzle<1,0,3,2>(vec) instead of (vec,YXWZ). At any rate, swizzling is defined in a separate header, don't include it if you don't want it.
YXWZ(vec) is not only much more readable than (vec,YXWZ), it's also one character shorter. -- Rainer Deyke (rainerd@eldwood.com)

On Wed, Dec 16, 2015 at 1:38 PM, Rainer Deyke
On 16.12.2015 22:21, Emil Dotchevski wrote:
The swizzling syntax must be terse or else it's useless. If you've written shader code you'll know utterly inadequate it is to require a syntax like swizzle<1,0,3,2>(vec) instead of (vec,YXWZ). At any rate, swizzling is defined in a separate header, don't include it if you don't want it.
YXWZ(vec) is not only much more readable than (vec,YXWZ), it's also one character shorter.
It's less conventional, in GLSL you just use vec.yxwz, and (vec,YXWZ) is quite close. Also, let's say you want to access the X coordinate of a vector, (vec,X) is more readable than X(vec). I do appreciate the concerns about overloading operator comma, I was also skeptical since this wasn't my idea. FWIW after years of use in actual development this overload hasn't caused any problems. That said, I'm fine with using a different notation for swizzling. Emil

On Wed, Dec 16, 2015 at 8:51 PM, Emil Dotchevski
On Wed, Dec 16, 2015 at 1:38 PM, Rainer Deyke
wrote: On 16.12.2015 22:21, Emil Dotchevski wrote:
The swizzling syntax must be terse or else it's useless. If you've written shader code you'll know utterly inadequate it is to require a syntax like swizzle<1,0,3,2>(vec) instead of (vec,YXWZ). At any rate, swizzling is defined in a separate header, don't include it if you don't want it.
YXWZ(vec) is not only much more readable than (vec,YXWZ), it's also one character shorter.
It's less conventional, in GLSL you just use vec.yxwz, and (vec,YXWZ) is quite close.
Also, let's say you want to access the X coordinate of a vector, (vec,X) is more readable than X(vec).
I do appreciate the concerns about overloading operator comma, I was also skeptical since this wasn't my idea. FWIW after years of use in actual development this overload hasn't caused any problems.
That said, I'm fine with using a different notation for swizzling.
IIRC the conclusion in SG14 was that swizzling is uncommon enough in regular programming to not warrant out-of-the-way special operators. In that most cases of its use are in shader languages and not in C++ itself. Did you consider the the array op: vec[X], vec[YXWZ]? It has the properties of both you and Rainer's ideas. -- -- Rene Rivera -- Grafik - Don't Assume Anything -- Robot Dreams - http://robot-dreams.net -- rrivera/acm.org (msn) - grafikrobot/aim,yahoo,skype,efnet,gmail

On Wed, Dec 16, 2015 at 7:01 PM, Rene Rivera
On Wed, Dec 16, 2015 at 8:51 PM, Emil Dotchevski
wrote:
On Wed, Dec 16, 2015 at 1:38 PM, Rainer Deyke
wrote: On 16.12.2015 22:21, Emil Dotchevski wrote:
The swizzling syntax must be terse or else it's useless. If you've written shader code you'll know utterly inadequate it is to require a syntax like swizzle<1,0,3,2>(vec) instead of (vec,YXWZ). At any rate, swizzling is defined in a separate header, don't include it if you don't want it.
YXWZ(vec) is not only much more readable than (vec,YXWZ), it's also one character shorter.
It's less conventional, in GLSL you just use vec.yxwz, and (vec,YXWZ) is quite close.
Also, let's say you want to access the X coordinate of a vector, (vec,X) is more readable than X(vec).
I do appreciate the concerns about overloading operator comma, I was also skeptical since this wasn't my idea. FWIW after years of use in actual development this overload hasn't caused any problems.
That said, I'm fine with using a different notation for swizzling.
IIRC the conclusion in SG14 was that swizzling is uncommon enough in regular programming to not warrant out-of-the-way special operators. In that most cases of its use are in shader languages and not in C++ itself.
A somewhat common use case is to change the dimensions of a vector: - (v,XYZ) to get a 3D view of a 4D vector, or (v,XZ) to get a 2D view of a 3D or 4D vector; - (v,XYZ0) or (v,XYZ1) to get a 4D view of a 3D vector. Its use in C++ for actual swapping of elements is not as common -- that's why it is defined in a separate header file. Yet when it is needed its use is usually intensive. It occurs at the boundaries between different components which sometimes require change of coordinates. QVM provides some additional view proxies for this use case: negr/negc that can be used to negate a row or column of a matrix, and swapr/swapc that can be used to swap two rows or columns of a matrix.
Did you consider the the array op: vec[X], vec[YXWZ]? It has the properties of both you and Rainer's ideas.
Yes, there have been numerous discussions on this list about what operator to use for swizzling. Op[ ] is ideal except that it must be a member function and thus it can't be defined non-intrusively. Cheers, Emil

Emil Dotchevski wrote:
On Wed, Dec 16, 2015 at 12:26 PM, Phil Endecott < spam_from_boost_dev@chezphil.org> wrote:
Actually since first looking I've found that it does have more functionality than I thought - for example, I've only just found that it has functions to create matrices representing rotations (but not any other affine transformations)
Not true, it can do translation and scaling as well. There is no shear support right now but that's easy to add if needed.
The expression trans_m(v) reinterprets v as a translation matrix.
Scaling is done by diag_m(v), which reinterprets v as a matrix whose diagonal is the elements of v with all other elements zero.
Ah, I've finally found those in the "Vector-to-matrix view proxies" page.
and to compute the inverse of a matrix (but not the determinant).
Not true, there is the determinant() function
My apologies, I should have noticed that one. I think I was under the impression that the page was ordered alphabetically, or something.
the point of QVM isn't to add to the mix but to define an environment where all of the existing types can be used safely together.
Well let's look at that. Say I have a couple of libraries that each define their own matrix classes: namespace abc { class matrix; matrix f(); }; namespace xyz : class matrix; void g(matrix m); }; Ideally, I'd like to be able to write simply xyz::g( abc::f() ); and have the abc::matrix magically converted into an xyz::matrix. How much does QVM help? I can't write that "ideal" code, but maybe this: abc::matrix m1 = abc::f(); xyz::matrix m2; qvm::assign(m2,m1); xyz::g(m2); I'm not really convinced that that is very useful. What QVM does let you do is to, for example, multiply two abc::matrices. But say libabc already had its own super-efficient matrix multiplication algorithm, e.g. a multiply(a,b) function. I don't think QVM has any way to use that in its operator* implementation.
The swizzling syntax must be terse or else it's useless. If you've written shader code you'll know utterly inadequate it is to require a syntax like swizzle<1,0,3,2>(vec) instead of (vec,YXWZ). At any rate, swizzling is defined in a separate header, don't include it if you don't want it.
Actually I have written quite a lot of OpenGL code. I asked before for examples of this and the only one I was given was converting colours between RGBA and BGRA. This is mainly just curiosity; I don't doubt that you do write such code. I am starting to wonder if what you're really trying to achieve is a GLSL-like DSEL, i.e. specifically to write C++ that looks like shader code. Quoting from another message:
Also, let's say you want to access the X coordinate of a vector, (vec,X) is more readable than X(vec).
I honestly find that point of view totally bizarre. The function call syntax is the most basic thing we have in "C family" languages, it's practically the first thing that any programmer learns. Yet you prefer to write (vec,X) which is completely opaque until you discover that operator, has been overloaded. If your motivation is to do whatever is necessary in order to put the name of the operation after the argument, just wait for "unified call syntax" or "extension methods" (n4165, n4174). Regards, Phil.

On Thu, Dec 17, 2015 at 9:50 AM, Phil Endecott < spam_from_boost_dev@chezphil.org> wrote:
Emil Dotchevski wrote:
the point of QVM isn't to add to the mix but to define an environment where all of the existing types can be used safely together.
Well let's look at that. Say I have a couple of libraries that each define their own matrix classes:
namespace abc { class matrix; matrix f(); };
namespace xyz : class matrix; void g(matrix m); };
Ideally, I'd like to be able to write simply
xyz::g( abc::f() );
and have the abc::matrix magically converted into an xyz::matrix. How much does QVM help? I can't write that "ideal" code, but maybe this:
abc::matrix m1 = abc::f(); xyz::matrix m2; qvm::assign(m2,m1); xyz::g(m2);
Obviously it isn't possible for QVM to change 3rd-party type definitions to define implicit conversions, but it does provide a conversion function that you can call as noted in http://zajo.github.io/boost-qvm/Interoperability.html: xyz::g( makexyz::matrix(abc::f()) ); The make<> function is not needed if any QVM type is involved: the qvm::mat<> type for example converts implicitly to any compatible matrix type, and so do all the view proxies; if you did xyz::g( transp(abc::f()) ) there is no need to call make<>.
What QVM does let you do is to, for example, multiply two abc::matrices. But say libabc already had its own super-efficient matrix multiplication algorithm, e.g. a multiply(a,b) function. I don't think QVM has any way to use that in its operator* implementation.
If your types define operators the compiler will always prefer them over anything QVM defines, so if you want to define any super efficient operation for any of the types you use, just define it and it'll work. QVM can still help by filling-in the gaps: you define the critical stuff, it'll provide everything else.
The swizzling syntax must be terse or else it's useless. If you've written
shader code you'll know utterly inadequate it is to require a syntax like swizzle<1,0,3,2>(vec) instead of (vec,YXWZ). At any rate, swizzling is defined in a separate header, don't include it if you don't want it.
Actually I have written quite a lot of OpenGL code. I asked before for examples of this and the only one I was given was converting colours between RGBA and BGRA. This is mainly just curiosity; I don't doubt that you do write such code.
Yes, colors are a good use case for swizzling. I've been thinking that it makes sense to add RGBA names for this. Anyway, if your point is that swizzling isn't needed in C++ as much as in GLSL, that's fine, just don't include qvm/sw.hpp. :)
I am starting to wonder if what you're really trying to achieve is a GLSL-like DSEL, i.e. specifically to write C++ that looks like shader code.
No, not at all.
Quoting from another message:
Also, let's say you want to access the X coordinate of a vector, (vec,X) is
more readable than X(vec).
I honestly find that point of view totally bizarre. The function call syntax is the most basic thing we have in "C family" languages, it's practically the first thing that any programmer learns. Yet you prefer to write (vec,X) which is completely opaque until you discover that operator, has been overloaded.
See the previous message I posted on this list about making X(v) equivalent to (v,X), I'm interested to know if this alleviates your concerns. Thanks, Emil
participants (4)
-
Emil Dotchevski
-
Phil Endecott
-
Rainer Deyke
-
Rene Rivera