N-arity operators in C++, also reducing temporaries. [Repost]

This text not as silly as my "Herb Sutter's operator = can be optimized", so please read it. I'm currently working on library "Lazy" which eases creation of classes which needs lazy evalution and n-arity operators to improve speed. The library's sample usage placed below. Please let me know what do you think about it. I'll appreciate any feedback on my library. Library description: Library eases creation of classes which needs lazy evalution and n-arity operators. Library features are: * n-arity operators support; * operator aliasing, such as a + -b will be computed as a - b; * custom operators; * no unnecessary temporaries during evalution and assigning. Library's sample usage: //Matrix.h #include "lazy.hpp" class transpose_tag {}; //Tag class class Matrix { LAZY_CLASS(Matrix) static lazy::type<Matrix> Matrix_; //Similar to Andrei Alexandrescu's Type2Type template public: Matrix(); Matrix(const Matrix &); Matrix & operator = (const Matrix &); void swap(void Matrix &); //Libray won't work without swap function. //If name of your swap function isn't swap then use //LAZY_CLASS_EX(Matrix, YourSwapFunctionName) //lazy constuctors which enables lazy operators Matrix(LAZY_OP(Matix_ + Matrix_)); //Matrix operator +(Matrix, Matrix) Matrix(LAZY_OP(Matrix_ * Matrix_)); //... Matrix(LAZY_OP(-Matrix_); Matrix(LAZY_OP(Matrix_ + Matrix_ * Matrix_)); //ternary operator //Now enable operators LAZY_ENABLE_OP(Matrix_ = Matrix_ + Matrix_); LAZY_ENABLE_OP(Matrix_ = Matrix_ * Matrix_); LAZY_ENABLE_OP(Matrix_ = -Matrix_); LAZY_ENABLE_OP(Matrix_ = Matrix_ + Matrix_ * Matrix_); //operator aliasing LAZY_ALIAS(Matrix + -Matrix_ = Matrix_ - Matrix_); //Custom operation Matrix(LAZY_CUSTOM_OP(transpose_tag, (Matrix_))); LAZY_ENABLE_CUSTOM_OP(transpose, Matrix_ = lazy::custom_op<transpose_tag>(Matrix_)); //same as Matrix transpose (const Matrix &) but there //won't be any temporary objects at all }; //Matrix.cpp Matrix::Matrix(LAZY_OP(Matix_ + Matrix_) op) { //Construct matrix object //lhs matrix = op._1 //rhs matrix = op._2 } Matrix::Matrix(LAZY_CUSTOM_OP(transpose_tag, (Matrix_) op) { //Construct matrix object tranposed to op._1 } //etc //Now the use of Matrix class Matrix a, b, c, d; //... a = b + c; //No tempories at all //Using normal practice it will take two temporary objects (no NRVO or RVO ) a = b + -c; //Will be evaluates as a = b - c; No temporaries again //Using normal practice it will take three temporary objects (no NRVO or RVO) a = b + c * d; //Will use ternary operator. No temporaries again a = transpose(b); //Custom lazy operation. No tempories again Maybe the library is very similar to uBLAS's matrix_expression and vector_expression. But with my approach there is no need to code matrix_expression and vector_expression at all and it is easy to create new classes which needs lazy evaluation and n-arity operators. I believe that my library should be very useful with big_int. So what do you think? Is there any similar approaches available? Is there any need of such library? Thanks in advance. -- Pavel Chikulaev

Pavel Chikulaev wrote:
This text not as silly as my "Herb Sutter's operator = can be optimized", so please read it.
I'm currently working on library "Lazy" which eases creation of classes which needs lazy evalution and n-arity operators to improve speed.
The library's sample usage placed below. Please let me know what do you think about it. I'll appreciate any feedback on my library.
Library description: Library eases creation of classes which needs lazy evalution and n-arity operators.
Library features are: * n-arity operators support; * operator aliasing, such as a + -b will be computed as a - b; * custom operators; * no unnecessary temporaries during evalution and assigning.
I'd like a library which simplifies the implementation of expression templates. However, I'd like it to be more general than you have outlined. For instance I might not want a + -b to be computed as a - b, but I'd like to have this as an option, and not just for this particular sequence of operations. Also, I don't think it needs to be so heavily macro-based. Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0hups$587$1@sea.gmane.org...
I'd like a library which simplifies the implementation of expression templates. However, I'd like it to be more general than you have outlined.
More general? Plese tell me what you mean.
For instance I might not want a + -b to be computed as a - b, but I'd like to have this as an option, and not just for this particular sequence of operations.
Also, I don't think it needs to be so heavily macro-based. The key feature of the library is to make library usage as simple as
If you put LAZY_ALIAS(Matrix_ + -Matrix_ = Matrix_ - Matrix_) then a + -b will be computed as a - b, else it won't. Another examples of LAZY_ALIAS: LAZY_ALIAS(- - Matrix_ = Matrix_); //- - a will be computed as a. LAZY_ALIAS(-Matrix_ + Matrix_ = Matrix._2 - Matrix._1); //-a + b will be computed as b -a. possible, and macros is the only way (AFAIK) to achieve such simplicity. Examples: 1) Matrix(LAZY_OP(Matrix_ + Matrix_)) or equal non-macro version Matrix(const lazy::binary_op<Matrix, lazy::predefined_ops::binary_plus, Matrix> &) 2) Matrix(LAZY_OP(Matrix_ + Matrix_ * Matrix_)) or equal non-macro version Matrix(const lazy::binary_op<Matrix, lazy::predefined_ops::binary_plus, lazy::binary_op <Matrix, lazy::predefined_ops::mutiply, Matrix>
&);
LAZY_ENABLE_OP expands to about 20 lines of code. So, do you really think it can be less macro-based and more readable at the same time? If you have any ideas who do it, please send me how to do it. -- Pavel Chikulaev

Pavel Chikulaev wrote:
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0hups$587$1@sea.gmane.org...
I'd like a library which simplifies the implementation of expression templates. However, I'd like it to be more general than you have outlined.
More general? Plese tell me what you mean.
See my next comment.
For instance I might not want a + -b to be computed as a - b, but I'd like to have this as an option, and not just for this particular sequence of operations.
If you put LAZY_ALIAS(Matrix_ + -Matrix_ = Matrix_ - Matrix_) then a + -b will be computed as a - b, else it won't.
Okay, this certainly wan't clear from you're feature summary. I'll admit I didn't study the sample usage; this was because the feature summary seemed inadequate. Now that I look at your sample, I wonder whether it is possible to support operators which take instances of different types. This would be essential for an expression templates library.
Also, I don't think it needs to be so heavily macro-based. The key feature of the library is to make library usage as simple as possible,
Of course.
and macros is the only way (AFAIK) to achieve such simplicity.
So, do you really think it can be less macro-based and more readable at the same time? If you have any ideas who do it, please send me how to do it.
Have you considered the approach taken by Boost.Operators? http://www.boost.org/libs/utility/operators.htm Also, I believe Fusion (the sucessor to Tuple) would be a valuable tool. I'm not saying that macros have no place in an expression templates library; in the iostreams library, a single macro invocation is used to make a Filter usable with operator|. Jonathan

Now that I look at your sample, I wonder whether it is possible to support operators which take instances of different types. This would be essential for an expression templates library. Example: class Vector;
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0i7jt$60a$1@sea.gmane.org... old class Matrix { static lazy::type<Vector> Vector_; //Actually there is no need to define it //It is possible to use LAZY_OP(lazy::type<Vector>() * lazy::type<Vector>()) //but defining static object AFAIK is the simpliest way. public: LAZY_OP(Matrix_ * Vector_); // Or Matrix_ * Vector_ I don't remember ENABLE_LAZY_OP(Matrix_ = Matrix_ * Vector_); }; //You can use all classes. Just wrap their types in lazy::type<> and use //LAZY_OP or ENABLE_LAZY_OP or ALIAS and so on. I'm predicting you're next question - Is templates supported? Yes. Example: template<typename T> class Matrix { static lazy::type<Matrix> Matrix_; template<typename U> struct Matrix__ : lazy::type<Matrix<U> > {}; public: template<typename U> LAZY_OP(Matrix_ + Matrix__<U>()); //A bit uglier, isn't it? };
Have you considered the approach taken by Boost.Operators? Not yet. I still don't know whether to make += lazy operator or code += operator as usual.
Also, I believe Fusion (the sucessor to Tuple) would be a valuable tool. Is it (same as)(old version of) Tuple?
-- Pavel Chikulaev

Pavel Chikulaev wrote:
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0i7jt$60a$1@sea.gmane.org...
Now that I look at your sample, I wonder whether it is possible to support operators which take instances of different types. This would be essential for an expression templates library. Example:
<snip example> Okay, good.
I'm predicting you're next question - Is templates supported? Yes. Example: template<typename T> class Matrix { static lazy::type<Matrix> Matrix_;
template<typename U> struct Matrix__ : lazy::type<Matrix<U> > {}; public:
template<typename U> LAZY_OP(Matrix_ + Matrix__<U>()); //A bit uglier, isn't it? };
What about templates with more than one parameter: template<typename U, typename V> LAZY_OP(Matrix_ + Matrix__<U, V>()); // unprotected comma
Have you considered the approach taken by Boost.Operators? Not yet. I still don't know whether to make += lazy operator or code += operator as usual.
All operators should be lazy, for maximum generality.
Also, I believe Fusion (the sucessor to Tuple) would be a valuable tool. Is it (same as)(old version of) Tuple?
No. (What would be the point, then?) It provides extensible tuple-like sequences, together with a collectin of algorithms and adapters, similar to MPL. I think it's not documented, but last time I looked at it, the design was close enough to MPL that if you knew MPL and Tuple it would be easy to start using. The reason I though Fusion would be relevant is that in an expression template library, the information about a complex expression that you need to maintain in order for it to be evaluated once it is complete is: 1. A list of all the concrete objects which particpate in the expression 2. The structure of the expression. Fusion may provide the infrastructure for this. In the iostreams library, I used something like a homemade tuple; but for a more ambitious implementation of expression templates I would want to use existing tools as much as possible. Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0iarn$gpf$1@sea.gmane.org...
What about templates with more than one parameter:
template<typename U, typename V> LAZY_OP(Matrix_ + Matrix__<U, V>()); // unprotected comma
Right now I can tell you only one solution: template<typename U, typename V> LAZY_OP((Matrix_ + Matrix__<U, V>())); How do you like it?
Also, I believe Fusion (the sucessor to Tuple) would be a valuable tool. Is it (same as)(old version of) Tuple?
No. (What would be the point, then?) It provides extensible tuple-like Oops. My bad - bad english. sequences, together with a collectin of algorithms and adapters, similar to MPL.
I've not looked at Fusion's algorithms but I'm pretty sure that it won't help me with parsing operators tree and choosing the most conforming lazy operator.
I think it's not documented, but last time I looked at it, the design was close enough to MPL that if you knew MPL and Tuple it would be easy to start using.
The reason I though Fusion would be relevant is that in an expression template library, the information about a complex expression that you need to maintain in order for it to be evaluated once it is complete is:
1. A list of all the concrete objects which particpate in the expression 2. The structure of the expression.
Fusion may provide the infrastructure for this. In the iostreams library, I used something like a homemade tuple; but for a more ambitious implementation of expression templates I would want to use existing tools as much as possible.
I've used in my library home-made template<typename FirstArgT, typename Op, typename SecondArgT> class binary_op; template<typename Op, typename ArgT> class unary_op; template<typename LazyExpr> class result_of; I don't think that Fusion will help me here, but I'll take a look at it. Thanks. -- Pavel Chikulaev

Pavel Chikulaev wrote:
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0iarn$gpf$1@sea.gmane.org...
What about templates with more than one parameter:
template<typename U, typename V> LAZY_OP(Matrix_ + Matrix__<U, V>()); // unprotected comma
Right now I can tell you only one solution: template<typename U, typename V> LAZY_OP((Matrix_ + Matrix__<U, V>())); How do you like it?
Fine.
Also, I believe Fusion (the sucessor to Tuple) would be a valuable tool. Is it (same as)(old version of) Tuple?
No. (What would be the point, then?) It provides extensible tuple-like Oops. My bad - bad english. sequences, together with a collectin of algorithms and adapters, similar to MPL.
I've not looked at Fusion's algorithms but I'm pretty sure that it won't help me with parsing operators tree and choosing the most conforming lazy operator.
I've used in my library home-made
template<typename FirstArgT, typename Op, typename SecondArgT> class binary_op;
template<typename Op, typename ArgT> class unary_op;
template<typename LazyExpr> class result_of;
I don't think that Fusion will help me here, but I'll take a look at it. Thanks.
Okay, it way just a suggestion Jonathan

Pavel Chikulaev wrote:
Fusion may provide the infrastructure for this. In the iostreams library, I used something like a homemade tuple; but for a more ambitious implementation of expression templates I would want to use existing tools as much as possible.
I've used in my library home-made
template<typename FirstArgT, typename Op, typename SecondArgT> class binary_op;
template<typename Op, typename ArgT> class unary_op;
template<typename LazyExpr> class result_of;
I don't think that Fusion will help me here, but I'll take a look at it.
IIUC Fusion won't help you here as much as Phoenix (the successor to Boost::LL) which already contains many of building blocks you'd need for your library. Regards Hartmut

Hartmut Kaiser wrote:
Pavel Chikulaev wrote:
Fusion may provide the infrastructure for this. In the iostreams library, I used something like a homemade tuple; but for a more ambitious implementation of expression templates I would
[...]
I don't think that Fusion will help me here, but I'll take a look at it.
IIUC Fusion won't help you here as much as Phoenix (the successor to Boost::LL) which already contains many of building blocks you'd need for your library.
Sure it will! In as much as Phoenix2 is built using Fusion, sure, I don't see why Fusion can't help here. We've seen how nifty it was for Spirit2 to be built on Fusion too. Composing the ET nodes, for example and doing the transforms on the composites was done quite elegantly thanks to Fusion. Think composites, think Fusion. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Joel de Guzman wrote:
IIUC Fusion won't help you here as much as Phoenix (the successor to Boost::LL) which already contains many of building blocks you'd need for your library.
Sure it will!
In as much as Phoenix2 is built using Fusion, sure, I don't see why Fusion can't help here. We've seen how nifty it was for Spirit2 to be built on Fusion too. Composing the ET nodes, for example and doing the transforms on the composites was done quite elegantly thanks to Fusion. Think composites, think Fusion.
Yeah, agreed. It was my inconsise formulation above, which wasn't helpful ;-) All I wanted to say is that besides Fusion there is also Phoenix, which provides useful primitives for solving ET problems as well. Sorry for the misunderstanding. Regards Hartmut

It looks like that I should have learnt Boost more before I get started my library. I'll take a look at Phoenix too. Thanks. -- Pavel Chikulaev

Hi Jonathan, Thanks a lot for your feedback. It looks like you're almost the one who is interested in finding out what I am proposing. So I have a question: Is it really hard to find out what I am proposing on the first post? Bad English? -- Pavel Chikulaev

Pavel Chikulaev wrote:
Hi Jonathan,
Thanks a lot for your feedback.
You're welcome.
It looks like you're almost the one who is interested in finding out what I am proposing.
So I have a question: Is it really hard to find out what I am proposing on the first post? Bad English?
I don't think so. But it might have helped if you had mentioned expression templates explicitly and had included more exposition. Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0iavk$h6e$1@sea.gmane.org...
But it might have helped if you had mentioned expression templates explicitly and had included more exposition.
Do you think I should repost fixed proposal with extended sample usage? Is "expression templates" well-known term for what I am doing? Could you tell what does it mean?

Pavel Chikulaev wrote:
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0iavk$h6e$1@sea.gmane.org...
But it might have helped if you had mentioned expression templates explicitly and had included more exposition.
Do you think I should repost fixed proposal with extended sample usage?
I think think you should just wait for people to catch up with their list reading.
Is "expression templates" well-known term for what I am doing?
Yes, I believe so.
Could you tell what does it mean?
See, e.g., http://osl.iu.edu/~tveldhui/papers/Expression-Templates/exprtmpl.html Also the Google query "Expression Templates" produces lots of results. You should also look at Daixtrose. http://daixtrose.sourceforge.net/ Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0if58$v09$1@sea.gmane.org...
http://osl.iu.edu/~tveldhui/papers/Expression-Templates/exprtmpl.html
I use same technique in my library. I came to it without any articles about expression templates, but I use same names with example from that article. Amazing! But N-arity operations do not come out easily from expression templates and parsing tree on more than one level is the most challenging part of my library. Even now I still have minor problems with it.
It looks like they parse that tree only one level, but I've downloaded it to have a closer look. -- Pavel Chikulaev

"Pavel Chikulaev" <pavel.chikulaev@gmail.com> wrote in message news:d0i9ia$ch9$1@sea.gmane.org...
Hi Jonathan,
Thanks a lot for your feedback. It looks like you're almost the one who is interested in finding out what I am proposing.
So I have a question: Is it really hard to find out what I am proposing on the first post? Bad English?
Patience my friend. Do you think people seat and wait for new proposals to discuss? Everybody have their own priorities. Some people read ML only sporadically. It may take while. Meanwhile it seems (at least IMHO) that you could read archive on long (very long in fact) history of discussions on lazy evaluation for both temporary elimination and in application to expression templates. You may be interested in: mojo, zetu, FP++, several posts by Dave Abrahams on the topic. I believe Andrej has an article online also. We have already libs for matrix operation and Dave A. I know works in this direction also. HTH, Gennadiy

"Gennadiy Rozental" <gennadiy.rozental@thomson.com> wrote in message news:d0iaq7$gjl$1@sea.gmane.org...
Patience my friend. Do you think people seat and wait for new proposals to discuss? Everybody have their own priorities. Some people read ML only sporadically. It may take while.
Thanks. I'll wait.
Meanwhile it seems (at least IMHO) that you could read archive on long (very long in fact) history of discussions on lazy evaluation for both temporary elimination and in application to expression templates. You may be interested in: mojo, zetu, FP++, several posts by Dave Abrahams on the topic. I believe Andrej has an article online also. We have already libs for matrix operation and Dave A. I know works in this direction also.
I'm pretty proud to tell you that there is no tempories at all. Except copying references to arguments before assigning or evaluation. But it cost almost nothing and can be greatly optimized. -- Pavel Chikulaev

Pavel Chikulaev wrote:
"Gennadiy Rozental" <gennadiy.rozental@thomson.com> wrote in message news:d0iaq7$gjl$1@sea.gmane.org...
Patience my friend. Do you think people seat and wait for new proposals to discuss? Everybody have their own priorities. Some people read ML only sporadically. It may take while.
Thanks. I'll wait.
Meanwhile it seems (at least IMHO) that you could read archive on long (very long in fact) history of discussions on lazy evaluation for both temporary elimination and in application to expression templates. You may be interested in: mojo, zetu, FP++, several posts by Dave Abrahams on the topic. I believe Andrej has an article online also. We have already libs for matrix operation and Dave A. I know works in this direction also.
I'm pretty proud to tell you that there is no tempories at all. Except copying references to arguments before assigning or evaluation. But it cost almost nothing and can be greatly optimized.
I think Gennadiy is including temporaries that can result if you return an expression from a function, i.e., the kind of temporaries that can often be eliminated with move semantics. Jonathan

Jonathan Turkanis wrote:
I'm pretty proud to tell you that there is no tempories at all. Except copying references to arguments before assigning or evaluation. But it cost almost nothing and can be greatly optimized.
I think Gennadiy is including temporaries that can result if you return an expression from a function, i.e., the kind of temporaries that can often be eliminated with move semantics.
This didn't come out right; expression templates can help with this too. Still, combining move semantics and expression templates could be very powerful. Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0iedi$sgf$1@sea.gmane.org...
I think Gennadiy is including temporaries that can result if you return an expression from a function, i.e., the kind of temporaries that can often be eliminated with move semantics.
Even with lazy functions (or custom operations) there is no temporaries at all.

Pavel Chikulaev wrote:
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0iedi$sgf$1@sea.gmane.org...
I think Gennadiy is including temporaries that can result if you return an expression from a function, i.e., the kind of temporaries that can often be eliminated with move semantics.
Even with lazy functions (or custom operations) there is no temporaries at all.
I'm talking about performing a calculation in the body of a function and then returning the result. Unless a form of the return value optimization applies, you may have a temporary object. Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0klpu$t3m$1@sea.gmane.org...
Even with lazy functions (or custom operations) there is no temporaries at all.
I'm talking about performing a calculation in the body of a function and then returning the result. Unless a form of the return value optimization applies, you may have a temporary object.
Believe me, there is no temporaries at all. I already use move semantics. This support is embedded in my library. -- Pavel Chikulaev

Pavel Chikulaev wrote:
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0klpu$t3m$1@sea.gmane.org...
Even with lazy functions (or custom operations) there is no temporaries at all.
I'm talking about performing a calculation in the body of a function and then returning the result. Unless a form of the return value optimization applies, you may have a temporary object.
Believe me, there is no temporaries at all. I already use move semantics. This support is embedded in my library.
Cool! This would have been another good buzzword to stick in your library description. BTW, how does your library implement move semantics? Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0l4o5$jf9$1@sea.gmane.org...
Cool! This would have been another good buzzword to stick in your library description. BTW, how does your library implement move semantics?
Since my library controls everything: I mean all operators, including assignment operator I can do almost everything. E.g. move semantics. -- Pavel Chikulaev

Pavel Chikulaev wrote:
This text not as silly as my "Herb Sutter's operator = can be optimized", so please read it.
In addition to what we've discussed. 1. You might way to include a general treatment of placeholder symbols (_1, _2, _3, ...) in you're library, to help reduce the number of different conflicting definitions from various libraries. 2. You might provide native support for reference wrappers, from Boost.Ref (or TR1). Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0iend$tg3$1@sea.gmane.org...
1. You might way to include a general treatment of placeholder symbols (_1, _2, _3, ...) in you're library, to help reduce the number of different conflicting definitions from various libraries. I use placeholder symbols (_1, _2, _3, ...) as public static members of lazy::type<T>. So there shouln't be such problems.
What do you think which statement is more readable and understandable? LAZY_ALIAS(-Matrix_ + Matrix_ = Matrix_._2 - Matrix_._1); or LAZY_ALIAS(-Matrix_ + Matrix_ = _2 - _1); If second then I better include such support.
2. You might provide native support for reference wrappers, from Boost.Ref (or TR1).
Matrix a, b, c, d, e; //.. a = (b + c * d * (d + -b + - - c); Expression template on rhs operators only with references to a, b, c, d, e. Also I use boost::call_traits<T>::param_type where building expession template. So I think there is no need for such support. Maybe I misunderstand something? -- Pavel Chikulaev

Pavel Chikulaev wrote:
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0iend$tg3$1@sea.gmane.org...
1. You might way to include a general treatment of placeholder symbols (_1, _2, _3, ...) in you're library, to help reduce the number of different conflicting definitions from various libraries. I use placeholder symbols (_1, _2, _3, ...) as public static members of lazy::type<T>. So there shouln't be such problems.
What do you think which statement is more readable and understandable? LAZY_ALIAS(-Matrix_ + Matrix_ = Matrix_._2 - Matrix_._1); or LAZY_ALIAS(-Matrix_ + Matrix_ = _2 - _1);
If second then I better include such support.
2. You might provide native support for reference wrappers, from Boost.Ref (or TR1).
Matrix a, b, c, d, e; //.. a = (b + c * d * (d + -b + - - c);
Expression template on rhs operators only with references to a, b, c, d, e. Also I use boost::call_traits<T>::param_type where building expession template. So I think there is no need for such support. Maybe I misunderstand something?
My point is that if you want existing libraries or new libraries patterned after existing Boost libraries to use your infrastructure, you should provide support for placeholders and reference wrappers to be used as part of the public interface of such libraries. E.g., imagine that Boost.Lambda were rewritten to use your library. Could your _1, _2 etc be used in all the ways they are currently used by Boost.Lambda? Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:d0km5p$ugn$1@sea.gmane.org...
My point is that if you want existing libraries or new libraries patterned after existing Boost libraries to use your infrastructure, you should provide support for placeholders and reference wrappers to be used as part of the public interface of such libraries. E.g., imagine that Boost.Lambda were rewritten to use your library. Could your _1, _2 etc be used in all the ways they are currently used by Boost.Lambda?
Not yet. Need to think about it. Thanks. -- Pavel Chikulaev

Hi Pavel, part of my code base uses a similar approach for linear algebra operations and I'm potentially interested to retire some of the code if there is a boost-quality library available for this. Some of the requirements for such a framework to be suitable for my needs are: - Custom per element operations As a simple example think of homogenous vectors (an x or o products require an additional multiplication of w-elements, additive operations requires scale by (1/w)) another example would be a 90 degree rotation of a vector by swapping/negating its elements. - It should not be limited to operators but work with any functions In real-life situations operators may be too cryptic for some applications (or there aren't enough of them). - SIMD "overrides" The design should allow to optionally replace operations to make use of SIMD hardware. Regards, Tobias

"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:d0igjh$46u$1@sea.gmane.org...
- Custom per element operations You mean matrix<double> m; double d = transpose(m)[5][6]; // don't you? It's not yet supported, but I think I can do it.
Thanks.
- It should not be limited to operators but work with any functions
N-Arity Functions are already supported. It is so called custom operations. Transpose function is example from sample usage. Another example: class product_t; same class Matrix { //.. Matrix(LAZY_CUSTOM_OP(product_tag, (2, (Matrix_, Matrix_))); LAZY_ENABLE_CUSTOM_OP(product, Matrix_ = lazy::custom_op<product_tag>((Matrix_, Matrix_))); }; Matrix::Matrix(LAZY_CUSTOM_OP(product_tag, (2, (Matrix_, Matrix_)) op) { //first matrix accessed via op._1 //second matrix accessed via op._2 } //.. Matrix a, b, c; //.. a = product(b, c); //No temporaries The only drawback of such approach is that I can't add a lazy function without changing class. But I'm working on it. I think it's not necessary to change class definition. Eurika! I know how to do it. Template specialiation will help me again. Thanks again!
The design should allow to optionally replace operations to make use of SIMD hardware.
Library also supports it. AFAIK It's template specialization of lazy operator, isn't it? -- Pavel Chikulaev

Pavel Chikulaev wrote:
"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:d0igjh$46u$1@sea.gmane.org...
- Custom per element operations
You mean matrix<double> m; double d = transpose(m)[5][6]; // don't you? It's not yet supported, but I think I can do it.
That's the special case where all elements are used. I use 'apply', 'apply_one' and 'apply_but' functions that allow me to apply functors to all elements, one element and all but one elements, respectively (I use different versions for both static and dynamic indices and both static and dynamic functors): // Create an expression inverting the first element // of my_vector (using Boost.Lambda) apply_one<0> (my_vector, - _1) Further the result type may not always be the same as the argument type: vector_slice<0,3> (my_quaternion) = direction * std::sin(a*half);
The design should allow to optionally replace operations to make use of SIMD hardware.
Library also supports it. AFAIK It's template specialization of lazy operator, isn't it?
It can be difficult to add such specializations - depending on how your expression templates are implemented. It's just something to keep in mind. And while we're at it, and because it seems you found the previous post inspiring, here is some more: - There should be an implicit conversion sequence that allows to evaluate a lazy expression: ( Guess you thought of this one - mentioning it just in case ) xpd(direction * std::sin(a*half), vector_slice<0,3>(my_quaternion)) It's nice when the cross product implementation can indicate by the paramter type of its signature that it needs an intermediate result (otherwise it would have to evalutate the expression explicitly or direction * std::sin(a*half) will be evaluated twice). - Do you know the Boost.Assign technique ? I use a similar approach to have a nice syntax for initialization and inlining that works within expressions: my_quaternion = n_x * sin_half_a , n_y * sin_half_a , n_z * sin_half_a , cos_half_a; and vector_slice<0,3> (my_quaternion) = (vec_inl| n_x,n_y,0.0) * sin_half_a; ( The 'my_quaternion' variable from the examples is a four-dimensional vector, _not_ a boost::quaternion. ) Regards, Tobias

"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:d0k5m1$4cp$1@sea.gmane.org...
It can be difficult to add such specializations - depending on how your expression templates are implemented. It's just something to keep in mind.
Example: template<typename T, int Rows, int Cols> class matrix { LAZY_CLASS(matrix); static lazy::type<matrix> matrix_; public: //.. matrix(LAZY_OP(matrix_ + matrix_)); //.. }; template<typename T, int Rows, int Cols> matrix<T, Rows, Cols>::matrix(LAZY_OP(matrix_ + matrix_) op) { //.. } template<> matrix<int, 4, 4>::matrix(LAZY_OP(matrix_ + matrix_) op) { //Using SIMD instructions. } Enough easy?
- There should be an implicit conversion sequence that allows to evaluate a lazy expression: Already works. Example: void fun(Matrix &); Matrix a, b, c; fun(a + b * c * (a + b)); //compiles just fine.
It's nice when the cross product implementation can indicate by the paramter type of its signature that it needs an intermediate result (otherwise it would have to evalutate the expression explicitly or direction * std::sin(a*half) will be evaluated twice).
It won't be evaluated twice, AFAIK.
- Do you know the Boost.Assign technique ? I use a similar approach to have a nice syntax for initialization and inlining that works within expressions: I believe that this is not within coverage of my library. My library deals with lazy evaluation of functions and n-arity operators. Maybe I misundersand something?
-- Pavel Chikulaev

"Pavel Chikulaev" <pavel.chikulaev@gmail.com> wrote in message news:d0k7sp$au4$1@sea.gmane.org...
- There should be an implicit conversion sequence that allows to evaluate a lazy expression: Already works. Example: void fun(Matrix &); Matrix a, b, c; fun(a + b * c * (a + b)); //compiles just fine.
Oops. It won't work. void fun(const Matrix &); Matrix a, b, c; fun(a + b * c * (a + b)); // Now it works. -- Pavel Chikulaev

Pavel Chikulaev wrote:
"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:d0k5m1$4cp$1@sea.gmane.org...
- Do you know the Boost.Assign technique ? I use a similar approach to have a nice syntax for initialization and inlining that works within expressions:
I believe that this is not within coverage of my library. My library deals with lazy evaluation of functions and n-arity operators.
Well, the initialization part probably not, but...
Maybe I misundersand something?
...the inline part needs special care to work within your expressions (can't use user-defined conversion before type deduction - but you'ld need exactly this to add this feature non-intrusively). This way the initialization part becomes "recycling code". Best, Tobias

"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:d0ka2v$hm4$1@sea.gmane.org...
Maybe I misundersand something?
...the inline part needs special care to work within your expressions (can't use user-defined conversion before type deduction - but you'ld need exactly this to add this feature non-intrusively). This way the initialization part becomes "recycling code".
I still can't get you point. Please explain more thoroughly. -- Pavel Chikulaev

Pavel Chikulaev wrote:
"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:d0ka2v$hm4$1@sea.gmane.org...
Maybe I misundersand something?
...the inline part needs special care to work within your expressions (can't use user-defined conversion before type deduction - but you'ld need exactly this to add this feature non-intrusively). This way the initialization part becomes "recycling code".
I still can't get you point. Please explain more thoroughly.
I needed all these features (which may seem strange to you) to make the use of my library convenient enough for "real life". However, these are just examples of functionality that is needed in one way or the other, sooner or later by one or the other (well - that's the challenge of generic programming isn't it ?) - look at it as a checklist to bulletproof your design (I am not trying to put a dozen of feature requests on your todo-list - it's enough if what I'm talking about is easily implementable). You're maybe right that this particular point is beyond the scope of your proposal. But it depends on how much you want the design to be a "black box" and on how properly it adresses the problems we are talking about here [ -> http://tinyurl.com/3wob5 ], I believe. This said, I try to explain in more detail: As long as our_vector is a non-template class with always three floats, for example - there are no problems at all. In this case we won't need such stuff at all because we'ld have a constructor with 3 paramters for initialization - this we can use inline and everything is just fine ;-). Now let our_vector be a template with parametrized dimensionality. In this case it's unlikely there is such a constructor. One way would be an overloaded factory function with different scalar types (a la "make_pair") with one overload for each possible dimensionality. This way has some advantages but isn't truely generic (unless you pull your genericity to preprocessing time ;-). N-ary element inlines are. The expression of (vec_inl|1,2,3) gives me an n-ary _expression_object_ which can work directly with lazy operators. I won't explain how it works, again [ just did this in another post in this thread http://tinyurl.com/6xhe6 ]. Regards, Tobias

"Pavel Chikulaev" <pavel.chikulaev@gmail.com> wrote in message news:d0k7sp$au4$1@sea.gmane.org...
It's nice when the cross product implementation can indicate by the paramter type of its signature that it needs an intermediate result (otherwise it would have to evalutate the expression explicitly or direction * std::sin(a*half) will be evaluated twice).
It won't be evaluated twice, AFAIK.
One more oops. It will be in only one rare case: xpd(LAZY_OP_RESULT_OF(your_vector)); //anything lazy-convertible to vector. //will not be evaluated before calling xpd But in most cases you should write: xpd(const your_vector &); //Lazy expression will be evaluated before //calling xpd Also have lazy::evaluate( ) function to evaluate lazy-expression whenever you want. -- Pavel Chikulaev

Pavel Chikulaev wrote:
"Pavel Chikulaev" <pavel.chikulaev@gmail.com> wrote in message news:d0k7sp$au4$1@sea.gmane.org...
It's nice when the cross product implementation can indicate by the paramter type of its signature that it needs an intermediate result (otherwise it would have to evalutate the expression explicitly or direction * std::sin(a*half) will be evaluated twice).
It won't be evaluated twice, AFAIK.
One more oops.
It will be in only one rare case: xpd(LAZY_OP_RESULT_OF(your_vector)); //anything lazy-convertible to vector. //will not be evaluated before calling xpd
But in most cases you should write: xpd(const your_vector &); //Lazy expression will be evaluated before //calling xpd
I guess I have to apologize for being quite unclear here. Sorry ! It becomes problematic if our_vector is a template ( <N,ScalarT> for example - btw. the cross product may be a bad example since it only applies to N == 3 ), because there is no way for this to work if the arguments are expression objects (because you can't have both deduction and user defined conversion at once). The possible ways around it are: a) The library provides the user with a way to deduce the "conceptual type" from expression objects, or b) friend name injection is used from inside the vector class so our_vector is a full specialization (== a type) and not a template anymore. The preferred solution is clearly point a, because it does not require the vector class to know all function that want to take a vector (== already evaluated expression in this context) as parameter.
Also have lazy::evaluate( ) function to evaluate lazy-expression whenever you want.
Well, that was quite easy to guess ;-). Regards, Tobias

On 03/08/2005 06:26 AM, Tobias Schwinger wrote: [snip]
I use 'apply', 'apply_one' and 'apply_but' functions that allow me to apply functors to all elements, one element and all but one elements, respectively (I use different versions for both static and dynamic indices and both static and dynamic functors):
What does "static" and "dynamic" mean here? Does static mean "evaluable at compile time" and "dynamic" means otherwise? [snip]
- Do you know the Boost.Assign technique ? I use a similar approach to have a nice syntax for initialization and inlining that works within expressions:
my_quaternion = n_x * sin_half_a , n_y * sin_half_a , n_z * sin_half_a , cos_half_a;
and
vector_slice<0,3> (my_quaternion) = (vec_inl| n_x,n_y,0.0) * sin_half_a;
Never seen this (vec_inl| n_x,n_y,0.0). Looks like a set expression or lambda expression. Could you provide a reference. I searched: http://www.boost.org/libs/assign/doc/index.html#reference for the character '|' but got no results.
( The 'my_quaternion' variable from the examples is a four-dimensional vector, _not_ a boost::quaternion. )
Does "four-dimensional" mean "length is four" or "accessing a scalar requires four indices passed to the operator()", e.g. my_quaternion(0,2,1,1) or something similar (I want to call the above operator() the the index operator, but can't for obvious reasons) ? [snip]

Larry Evans wrote:
On 03/08/2005 06:26 AM, Tobias Schwinger wrote: [snip]
I use 'apply', 'apply_one' and 'apply_but' functions that allow me to apply functors to all elements, one element and all but one elements, respectively (I use different versions for both static and dynamic indices and both static and dynamic functors):
What does "static" and "dynamic" mean here? Does static mean "evaluable at compile time" and "dynamic" means otherwise?
Yes. A curious question: what else could it possibly mean in this context ?
[snip]
- Do you know the Boost.Assign technique ? I use a similar approach to have a nice syntax for initialization and inlining that works within expressions:
my_quaternion = n_x * sin_half_a , n_y * sin_half_a , n_z * sin_half_a , cos_half_a;
and
vector_slice<0,3> (my_quaternion) = (vec_inl| n_x,n_y,0.0) * sin_half_a;
Never seen this (vec_inl| n_x,n_y,0.0). Looks like a set expression or lambda expression. Could you provide a reference. I searched:
http://www.boost.org/libs/assign/doc/index.html#reference
for the character '|' but got no results.
This is _not_ Boost.Assign! As stated above it's part of my private library but it has some similarities to Boost.Assign. I use this to allow per-element data to be inlined in expressions. 'operator|' is overloaded for a tag type (vec_inl is a global of that type) and scalar types, creates an expression object for which an overloaded 'operator,' exists to append elements.
( The 'my_quaternion' variable from the examples is a four-dimensional vector, _not_ a boost::quaternion. )
Does "four-dimensional" mean "length is four" or "accessing a scalar requires four indices passed to the operator()", e.g.
Is this really that ambiguous ? AFAIK it's a proper mathematical term... Besides quaternions consist of four scalars (so I might as well omitted 'four-dimensional'). I found it necessary to disclaim it's not a boost::quaternion, though... Regards, Tobias

Tobias Schwinger wrote:
Larry Evans wrote:
On 03/08/2005 06:26 AM, Tobias Schwinger wrote: [snip]
I use 'apply', 'apply_one' and 'apply_but' functions that allow me to apply functors to all elements, one element and all but one elements, respectively (I use different versions for both static and dynamic indices and both static and dynamic functors):
What does "static" and "dynamic" mean here? Does static mean "evaluable at compile time" and "dynamic" means otherwise?
Yes. A curious question: what else could it possibly mean in this context ?
[snip]
- Do you know the Boost.Assign technique ? I use a similar approach to have a nice syntax for initialization and inlining that works within expressions:
my_quaternion = n_x * sin_half_a , n_y * sin_half_a , n_z * sin_half_a , cos_half_a;
and
vector_slice<0,3> (my_quaternion) = (vec_inl| n_x,n_y,0.0) * sin_half_a;
Never seen this (vec_inl| n_x,n_y,0.0). Looks like a set expression or lambda expression. Could you provide a reference. I searched:
http://www.boost.org/libs/assign/doc/index.html#reference
for the character '|' but got no results.
This is _not_ Boost.Assign! As stated above it's part of my private library but it has some similarities to Boost.Assign.
I use this to allow per-element data to be inlined in expressions. 'operator|' is overloaded for a tag type (vec_inl is a global of that type) and scalar types, creates an expression object for which an overloaded 'operator,' exists to append elements.
( The 'my_quaternion' variable from the examples is a four-dimensional vector, _not_ a boost::quaternion. )
Does "four-dimensional" mean "length is four" or "accessing a scalar requires four indices passed to the operator()", e.g.
Is this really that ambiguous ? AFAIK it's a proper mathematical term...
^^^^^^^^ By reading my post again this sounds a bit rude. No offense intended, here (as one unclear information in the middle of a text may cause a "domino effect" of doubt propagating through its tail).
Besides quaternions consist of four scalars (so I might as well omitted 'four-dimensional'). I found it necessary to disclaim it's not a boost::quaternion, though...
-- Tobias

"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:d0k5m1$4cp$1@sea.gmane.org...
That's the special case where all elements are used.
I use 'apply', 'apply_one' and 'apply_but' functions that allow me to apply functors to all elements, one element and all but one elements, respectively (I use different versions for both static and dynamic indices and both static and dynamic functors):
// Create an expression inverting the first element // of my_vector (using Boost.Lambda) apply_one<0> (my_vector, - _1)
Further the result type may not always be the same as the argument type:
vector_slice<0,3> (my_quaternion) = direction * std::sin(a*half);
I'm not sure such library needs 'apply', 'apply_one', 'apply_but'. User of my library will add them easily by himself if he really needs it, but in most case I don't think he is. -- Pavel Chikulaev

Pavel Chikulaev wrote:
"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:d0k5m1$4cp$1@sea.gmane.org...
I'm not sure such library needs 'apply', 'apply_one', 'apply_but'. User of my library will add them easily by himself if he really needs it,
That's all I wanted.
but in most case I don't think he is.
It was only an example for per-element operations in general. However, per-element operations are a must-have for real-life-programming and it shouldn't be to difficult for the user to implement them. -- Tobias

"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:d0munb$cjt$1@sea.gmane.org...
Pavel Chikulaev wrote:
"Tobias Schwinger" <tschwinger@neoscientists.org> wrote in message news:d0k5m1$4cp$1@sea.gmane.org...
I'm not sure such library needs 'apply', 'apply_one', 'apply_but'. User of my library will add them easily by himself if he really needs it,
That's all I wanted.
You didn't mean that it is everything you wanted from my library, do you?
but in most case I don't think he is.
It was only an example for per-element operations in general.
However, per-element operations are a must-have for real-life-programming and it shouldn't be to difficult for the user to implement them.
It's VERY VERY easy to implement such lazy operations by the user. When I come to documenting library I'll add lots of examples how to add such functionality. I promise. -- Pavel Chikulaev
participants (7)
-
Gennadiy Rozental
-
Hartmut Kaiser
-
Joel de Guzman
-
Jonathan Turkanis
-
Larry Evans
-
Pavel Chikulaev
-
Tobias Schwinger