
On Wed, Feb 25, 2004 at 04:41:42PM -0600, Max Motovilov wrote:
[Proposal, Part I]
Providing a facility that simplifies arithmetic type promotions for D.A.T.s. None of the examples above currently provide it, which means that you cannot expect
std::complex<float>( 1, 0 ) + std::complex<double>( 0, 1 )
to yield std::complex<double>( 1, 1 ). So, expressions on D.A.T.s with mixed base types do not really behave the way the built-in numeric types do, even though the individual values can be converted from one instance of the same D.A.T. to another.
In the ideal world, one should be able to write:
template< typename T1, typename T2 > foo< typeof( T1() + T2() ) > operator+( const foo<T1>&, const foo<T2>& );
and then implement operator+ the way it is normally implemented for foo<T>, first converting foo<T1> and foo<T2> to foo< typeof< T1() + T2() >. I intentionally leave out the optimization issues such as defining operator+ in terms of operator+= etc. However, without typeof(), it would be nice to have a library solution yielding similar result, for example:
template< typename T1, typename T2 > foo< promoted_type< T1, T2, std::plus > > operator+( const foo<T1>&, const foo<T2>& );
The third argument of promoted_type<> could be any kind of tag value or type associated with the type of arithmetic operation. Templates std::plus, std::minus, std::multiplies, std::divides and std::modulus do not cover the entire range of C++ arithmetic operations so some other tags may work better. The implementation of promoted_type will obviously be based on partial specializations along with a limited typeof() facility with the assumption that T1 op T2 always yields either T1 or T2 (which is the case for built-in base types). Here is a quick and dirty implementation for only one operation, operator+:
See, e.g. http://www.boost.org/libs/lambda/doc/ar01s06.html operator-tag representations as well as the logic that "when combining float and double, promote both to double" already exist somewhere in Boost (ask Joel or Jaakko, I dunno the details), so reuse those. ...
template< typename T1, typename T2, template< typename _ > class Tag
struct promoted_type;
My hunch is that this bit will work better if you use MPL (rather than have template-template parameters, you can treat the class as a metafunction (e.g. std::complex<_1>) which can be "apply"ed.
template< typename T1, typename T2 > struct promoted_type< T1, T2, std::plus
{ enum { tag = sizeof( typeof_one_of_two<T1,T2>::typeof_fun( T1() + T2() ) ) }; typedef typename typeof_one_of_two<T1,T2>::select_type< tag >::type type; };
Also, here you presume default constructors for T1 and T2; make sure the actual implementation avoid this.
Going further, we might want to add type-promoting arithmetics to existing DATs that do not provide it. The best I could think of so far is introducing overly generic template operators into global namespace, along with a mechanism based on boost::enable_if to restrict their use to specific parameter templates. The model implementation follows. I find it ugly, but perhaps a better one may be immediately obvious to somebody else. ... template< typename T1, typename T2, template< typename _ > class DAT > inline typename boost::disable_if_c< do_not< promote_sum< DAT > >::value, DAT< typename promoted_type<T1,T2,std::plus>::type >
::type operator+( const DAT<T1>& a, const DAT<T2>& b ) { typedef typename DAT< promoted_type<T1,T2,std::plus>::type > T; return T( a ) + T( b ); }
I'm sure there's a bit to hammer out with respect to the details, but yeah, I like the idea of using a specialization to say "I want this trick enabled" and then using enable_if with a general operator@() template to do the automatic promotion work.
Well, if anybody got this far, I have to confess that I'm not sure what to do with all this. The first part looks so trivial I may have well overlooked an equivalent facility in one of the existing Boost libraries. If I haven't, it hardly deserves a separate library, but I'm not sure which existing one would be a good place for it. The second part looks like it belongs in boost::operators, assuming that its benefits outweigh the dangers of introducing very generic operators into global namespace (or is there a way to do it otherwise and still get C++ to find the operator definitions?). Let me know whether it will be worthwhile to develop proposed facilities for subsequent Boostification.
There may already be some way to do this in Boost, I dunno. I think no, but I also think that sufficient related infrastructure is already there to do most of the work; this is probably easy to build on top, and if you "do it right", it will generalize really nicely. -- -Brian McNamara (lorgon@cc.gatech.edu)