[proto] Question on grammar and operator overloading
Hello, I'm writing a complex number class using boost::proto by extending proto::terminal< boost::array<_, 2> >. I've attached a copy of my current code, which compiles, runs, and (at least with both g++ and icc on my machine) demonstrates a significant speed improvement compared to std::complex. However, there is (at least) one thing which don't work as it should: In the grammar I have to specify lazy_complex< double > instead of lazy_complex< _ >, otherwise the compiler cannot find proto's operator overloads, why? Note: This code will not compile under 1.37, but will compile under 1.40, because of some issue in 1.37 which manifests itself as a problem with the overloading of the '<<' operator. I've not tested the releases in between 1.37 and 1.40. General suggestions are also welcome. I'd like to get this cleaned up for general redistribution. Thank you in advance, Hal
Hal Finkel wrote:
Hello,
I'm writing a complex number class using boost::proto by extending proto::terminal< boost::array<_, 2> >. I've attached a copy of my current code, which compiles, runs, and (at least with both g++ and icc on my machine) demonstrates a significant speed improvement compared to std::complex.
However, there is (at least) one thing which don't work as it should:
In the grammar I have to specify lazy_complex< double > instead of lazy_complex< _ >, otherwise the compiler cannot find proto's operator overloads, why?
In your grammar, lazy_complex<_> is equivalent to proto::terminal< boost::array< _, 2 > >; that is, they describe the same set of expression types. Unfortunately, Proto cannot handle a grammar like that. The problem is the "2". Proto can only peer inside template instantiations as long as none of the template parameters are non-type template parameters. There are a number of hackish solutions. You could define your own type: template<typename T, typename Size = mpl::int_<2> > struct my_array : boost::array< T, Size::value > {}; and use my_array throughout. Then your grammar becomes proto::terminal< boost::array< _, mpl::int_<2> > >, or just proto::terminal< boost::array< _, _ > >. The other alternative is to define a predicate that matches boost::array types: template<typename T> struct is_boost_array : mpl::false_ {}; template<typename T, int I> struct is_boost_array<boost::array<T,I> > : mpl::true_ {}; and change your grammar to: proto::and_< proto::terminal<_>, proto::if_<is_boost_array<proto::_value>()>
Either way. You could even just use some other terminal type besides boost::array. std::complex or std::pair would do.
Note: This code will not compile under 1.37, but will compile under 1.40, because of some issue in 1.37 which manifests itself as a problem with the overloading of the '<<' operator. I've not tested the releases in between 1.37 and 1.40.
Yes, I've fixed some bugs in this area. -- Eric Niebler BoostPro Computing http://www.boostpro.com
Either way. You could even just use some other terminal type besides boost::array. std::complex or std::pair would do.
Just dropping in as I have the exact same code lying around. Using std::complex itself as the underlying type of complex terminal is actually a good idea. When you'll have to do thing like cos( z ), you may want to go cak to call std::cos on complex as on some platform it uses some intrinsic that are faster than rewriting the cosinus by yourself. Moreover std::complex isn't more than a pair of T anyway. -- ___________________________________________ Joel Falcou - Assistant Professor PARALL Team - LRI - Universite Paris Sud XI Tel : (+33)1 69 15 66 35
Joel, Do you mean proto-izing std::complex itself (or some derived class), or using (extending) proto::terminal < std::complex >? On a related note, is there a standard way (yet) of changing a single component of std::complex? I'm not sure that there is... A recent working group draft has: T real() const; void real(T); T imag() const; void imag(T); The Apache stdcxx has only: T real() const; T imag() const; And the GNU libstdc++ has: _Tp& real(); const _Tp& real() const; _Tp& imag(); const _Tp& imag() const; My rationale for using an array value type was to make the interpretation of the type as a 2-element array clean. I'm working with several scientific codes which contain complex types defined as: typedef double complex_t[2]; and I'm preparing to port them to use more modern C++. However, I think that your point about the use of intrinsic functions is a good one. -Hal On Tue, 2009-10-06 at 15:15 +0200, joel wrote:
Either way. You could even just use some other terminal type besides boost::array. std::complex or std::pair would do.
Just dropping in as I have the exact same code lying around. Using std::complex itself as the underlying type of complex terminal is actually a good idea. When you'll have to do thing like cos( z ), you may want to go cak to call std::cos on complex as on some platform it uses some intrinsic that are faster than rewriting the cosinus by yourself. Moreover std::complex isn't more than a pair of T anyway.
Hal Finkel wrote:
Do you mean proto-izing std::complex itself (or some derived class), or using (extending) proto::terminal < std::complex >?
The later
On a related note, is there a standard way (yet) of changing a single component of std::complex? I'm not sure that there is... i always did z.imag() = x; My rationale for using an array value type was to make the interpretation of the type as a 2-element array clean. I'm working with several scientific codes which contain complex types defined as: typedef double complex_t[2]; and I'm preparing to port them to use more modern C++. However, I think that your point about the use of intrinsic functions is a good one.
Well, I ended up having an additional tempalte parameter (much like deque) that may change the way the internal values are stored. complex<T> <- internally use std::complex complex<T, boost::array<T,2> > <- you guess ;) I had similar concerns (supporting strangely allocated or aligned data block) and I foudn this solution to be the easiest to put to work. -- ___________________________________________ Joel Falcou - Assistant Professor PARALL Team - LRI - Universite Paris Sud XI Tel : (+33)1 69 15 66 35
Eric, Thank you, I'll use the predicate approach for now. I like the idea of using an array base because I have a lot of existing C and C++ code which treats complex numbers as a 2 element array, and accesses the members using the [] operator. I think this is common in high-performance scientific codes. I'm afraid doing it another way could lead to the generation of a large number of conditionals which the optimizer can't figure out how to eliminate (meaning (idx ? imag() : real()), I'd have to test this on several platforms), or the introduction of a lot of casts. -Hal On Tue, 2009-10-06 at 08:56 -0400, Eric Niebler wrote:
Hal Finkel wrote:
Hello,
I'm writing a complex number class using boost::proto by extending proto::terminal< boost::array<_, 2> >. I've attached a copy of my current code, which compiles, runs, and (at least with both g++ and icc on my machine) demonstrates a significant speed improvement compared to std::complex.
However, there is (at least) one thing which don't work as it should:
In the grammar I have to specify lazy_complex< double > instead of lazy_complex< _ >, otherwise the compiler cannot find proto's operator overloads, why?
In your grammar, lazy_complex<_> is equivalent to proto::terminal< boost::array< _, 2 > >; that is, they describe the same set of expression types. Unfortunately, Proto cannot handle a grammar like that. The problem is the "2". Proto can only peer inside template instantiations as long as none of the template parameters are non-type template parameters.
There are a number of hackish solutions. You could define your own type:
template<typename T, typename Size = mpl::int_<2> > struct my_array : boost::array< T, Size::value > {};
and use my_array throughout. Then your grammar becomes proto::terminal< boost::array< _, mpl::int_<2> > >, or just proto::terminal< boost::array< _, _ > >.
The other alternative is to define a predicate that matches boost::array types:
template<typename T> struct is_boost_array : mpl::false_ {}; template<typename T, int I> struct is_boost_array<boost::array<T,I> > : mpl::true_ {};
and change your grammar to:
proto::and_< proto::terminal<_>, proto::if_<is_boost_array<proto::_value>()>
Either way. You could even just use some other terminal type besides boost::array. std::complex or std::pair would do.
Note: This code will not compile under 1.37, but will compile under 1.40, because of some issue in 1.37 which manifests itself as a problem with the overloading of the '<<' operator. I've not tested the releases in between 1.37 and 1.40.
Yes, I've fixed some bugs in this area.
Hal Finkel wrote:
Eric,
Thank you, I'll use the predicate approach for now. I like the idea of using an array base because I have a lot of existing C and C++ code which treats complex numbers as a 2 element array, and accesses the members using the [] operator. I think this is common in high-performance scientific codes. I'm afraid doing it another way could lead to the generation of a large number of conditionals which the optimizer can't figure out how to eliminate (meaning (idx ? imag() : real()), I'd have to test this on several platforms), or the introduction of a lot of casts.
If you're going that route, you could just drop boost::array and use native arrays: proto::terminal< double[2] >::type Then your grammar changes to: proto::and_< proto::terminal<_>, proto::if_<boost::is_array<proto::_value>()>
HTH, -- Eric Niebler BoostPro Computing http://www.boostpro.com
Eric, I'm trying this option, but I can't get it to work correctly (the compiler does not find the operator overloads when I require boost::is_array to be "true"). If you uncomment the appropriate 3 lines in the grammar definition in the attached source file, you'll see what I mean. Thanks again, Hal On Tue, 2009-10-06 at 12:32 -0400, Eric Niebler wrote:
Hal Finkel wrote:
Eric,
Thank you, I'll use the predicate approach for now. I like the idea of using an array base because I have a lot of existing C and C++ code which treats complex numbers as a 2 element array, and accesses the members using the [] operator. I think this is common in high-performance scientific codes. I'm afraid doing it another way could lead to the generation of a large number of conditionals which the optimizer can't figure out how to eliminate (meaning (idx ? imag() : real()), I'd have to test this on several platforms), or the introduction of a lot of casts.
If you're going that route, you could just drop boost::array and use native arrays:
proto::terminal< double[2] >::type
Then your grammar changes to:
proto::and_< proto::terminal<_>, proto::if_<boost::is_array<proto::_value>()>
HTH,
participants (3)
-
Eric Niebler
-
Hal Finkel
-
joel