Proposal: type-promoting arithmetics for derivative types

This is a long post, so please let me know if making it into a Web page and posting the URL is a better idea. ======================= [Terminology] Since I am not sure whether there is an accepted term for this kind of types, let me introduce one - "derivative arithmetic type": - D.A.T. is a templatized ADT with at least some arithmetic operations defined - D.A.T. is parameterized with a single type-parameter which has at least some arithmetic operations defined (usually it is a built-in numeric type, but not necessarily) - Arithmetic operations on D.A.T. are defined in terms of arithmetic operations on the base type - There is a conversion available between differently parameterized instances of D.A.T. The above certainly isn't a good "standardese" concept definition but it should be enough to illustrate the proposal. Examples of D.A.T.s are std::complex<>, boost::rational<>, boost::math::quaternion<> etc. However, the motivation for this proposal came from my experience with implementing geometric types such as point, rectangle etc. along with operations on them. [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+: template< typename T1, typename T2 > struct typeof_one_of_two { template<int N> struct select_type; template<> struct select_type<1> { typedef T1 type; }; template<> struct select_type<2> { typedef T2 type; }; static char (&typeof_fun( const T1& ))[1]; static char (&typeof_fun( const T2& ))[2]; }; template< typename T > struct typeof_one_of_two<T,T> { template<int N> struct select_type { typedef T type; }; static char typeof_fun( const T& ); }; template< typename T1, typename T2, template< typename _ > class Tag
struct promoted_type;
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; }; (I tried it on MSVC++ 7.1 and it works, though for some reason it doesn't compile if I drag the expression for promoted_type<>::tag into the declaration for promoted_type<>::type. With the intermediate enum value it works fine - a compiler bug?) Note that promoted_type<> may be used not just in binary operators but in any external function or a D.A.T. method, possibly with different D.A.T. parameters, as long as one can make a reasonable deduction of the result type based on certain arithmetic operation over base type values. Also note that all built-in binary operators defined for a pair of built-in numeric types produce results of the same type. This means we wouldn't really need 3rd parameter for promoted_type<> if the base types were always built-in numerics, but user-defined base types may break this assumption. [Proposal, Part II] 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< template< typename _ > class DAT > struct promote_sum { typedef void tag; }; template< typename P, typename E=void > struct do_not { enum { value = false }; }; template< typename P > struct do_not< P, typename P::tag > { enum { value = true }; }; 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 ); }
To enable promoted-type operator+ for a specific DAT (that already has some flavor of operator+ though without mixing base types), write: template<> struct promote_sum< MyDAT > {}; The use, but not implementation, is similar to the technique suggested by David Abrahams in boost::operators. This one is uglier but, I believe, standard-compliant. Template do_not<> does a SFINAE trick of its own because I couldn't figure out how to do the same thing with enable_if. And again, MSVC++ 7.1 swallows it whole. Here's what I used to test it: template<typename T> struct MyDAT_1 { friend MyDAT_1<T> operator+ ( const MyDAT_1<T>&, const MyDAT_1<T>& ); MyDAT_1( T ); template<typename T1> explicit MyDAT_1( const MyDAT_1<T1>& ); }; template<typename T> struct MyDAT_2 { friend MyDAT_2<T> operator+ ( const MyDAT_2<T>&, const MyDAT_2<T>& ); MyDAT_2( T ); template<typename T1> explicit MyDAT_2( const MyDAT_2<T1>& ); }; template<> struct promote_sum<MyDAT_1> {}; Here MyDAT_1 and MyDAT_2 are identical types; both define an operator+ for uniform arguments. Promoted operator+ is only enabled for MyDAT_1. Now, MyDAT_1<double> a(( MyDAT_1<int>( 1 ) + MyDAT_1<float>( 2.0 ) )); compiles, while MyDAT_2<double> b(( MyDAT_2<int>( 1 ) + MyDAT_2<float>( 2.0 ) )); produces the following error: e:\tmp\t.cpp(77): error C2678: binary '+' : no operator found which takes a left-hand operand of type 'MyDAT_2<T>' (or there is no acceptable conversion) with [ T=int ] which looks rather on-the-money to me. The example implementation above only covers operator+( Foo<T1>, Foo<T2> ). It has an obvious extension to all operator@( Foo<T1>, Foo<T2> ) as well as to other things, for example: template<> struct promote_product2< Matrix, Vector > {}; which can be implemented in the same way. An important special case is support for things like operator*( Matrix<T1>, T2 ) where T2 is presumed to be a scalar type and Matrix<T1>*T2 is expected to yield a Matrix< promoted_type< T1, T2, std::multiplies > >. A generic operator* can be provided for this case but it will only discriminate based on its first argument, which may be dangerously generic for a general-purpose facility... ================================= 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. Regards, ...Max...

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)

"Brian McNamara" <lorgon@cc.gatech.edu> wrote in message news:20040225235607.GA1327@gaia3.cc.gatech.edu...
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.
Looks exactly like what I was proposing, even the syntax is similar. I knew it had to be there somewhere :)
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-template parameters were incidental to using standard library "functors" as tags, but it looks like BLL has a complete solution for this part already.
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.
Point taken. While it is not unreasonable to expect an arithmetic type to have a default constructor, it is better not to impose an unnecessary requirement.
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.
I still have not decided whether I "trust" the SFINAE-based constructs enough to rely on them fully for protection against unintended implicit specializations... feedback from people who have more experience with enable_if et. al. would be greatly appreciated. Also, I don't like to have a library introduce operator definitions into global namespace but I have not yet invented a way to avoid it in this context. Any ideas? Regards, ...Max...

Max Motovilov wrote:
"Brian McNamara" <lorgon@cc.gatech.edu> wrote in message news:20040225235607.GA1327@gaia3.cc.gatech.edu...
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.
Looks exactly like what I was proposing, even the syntax is similar. I knew it had to be there somewhere :)
If you want a newer technology, there's one in the boost-sandbox. See: boost/utility/type_deduction.hpp and a test program: libs/utility/type_deduction_tests.cpp. Let me quote the commentary in type_deduction.hpp (long): Return Type Deduction [JDG Sept. 15, 2003] Before C++ adopts the typeof, there is currently no way to deduce the result type of an expression such as x + y. This deficiency is a major problem with template metaprogramming; for example, when writing forwarding functions that attempt to capture the essence of an expression inside a function. Consider the std::plus<T>: template <typename T> struct plus : public binary_function<T, T, T> { T operator()(T const& x, T const& y) const { return x + y; } }; What's wrong with this? Well, this functor does not accurately capture the behavior of the plus operator. 1) It does not handle the case where x and y are of different types (e.g. x is short and y is int). 2) It assumes that the arguments and return type are the same (i.e. when adding a short and an int, the return type ought to be an int). Due to these shortcomings, std::plus<T>(x, y) is a poor substitute for x + y. The case where x is short and y is int does not really expose the problem. We can simply use std::plus<int> and be happy that the operands x and y will simply be converted to an int. The problem becomes evident when an operand is a user defined type such as bigint. Here, the conversion to bigint is simply not acceptable. Even if the unnecessary conversion is tolerable, in generic code, it is not always possible to choose the right T type that can accomodate both x and y operands. To truly model the plus operator, what we need is a polymorphic functor that can take arbitrary x and y operands. Here's a rough schematic: struct plus { template <typename X, typename Y> unspecified-type operator()(X const& x, Y const& y) const { return x + y; } }; Now, we can handle the case where X and Y are arbitrary types. We've solved the first problem. To solve the second problem, we need some form of return type deduction mechanism. If we had the typeof, it would be something like: template <typename X, typename Y> typeof(X() + Y()) operator()(X const& x, Y const& y) const { return x + y; } Without the typeof facility, it is only possible to wrap an expression such as x + y in a function or functor if we are given a hint that tells us what the actual result type of such an expression is. Such a hint can be in the form of a metaprogram, that, given the types of the arguments, will return the result type. Example: template <typename X, typename Y> struct result_of_plus { typedef unspecified-type type; }; Given a result_of_plus metaprogram, we can complete our polymorphic plus functor: struct plus { template <typename X, typename Y> typename result_of_plus<X, Y>::type operator()(X const& x, Y const& y) const { return x + y; } }; The process is not automatic. We have to specialize the metaprogram for specific argument types. Examples: template <> struct result_of_plus<short, int> { typedef int type; }; template <typename T> struct result_of_plus<std::complex<T>, std::complex<T> > { typedef std::complex<T> type; }; To make it easier for the user, specializations are provided for common types such as primitive c++ types (e.g. int, char, double, etc.), and standard types (e.g. std::complex, iostream, std containers and iterators). To further improve the ease of use, for user defined classes, we can supply a few more basic specializations through metaprogramming using heuristics based on canonical operator rules (Such heuristics can be found in the LL and Phoenix, for example). For example, it is rather common that the result of x += y is X& or the result of x || y is a bool. The client is out of luck if her classes do not follow the canonical rules. She'll then have to supply her own specialization. The type deduction mechanism demostrated below approaches the problem not through specialization and heuristics, but through a limited form of typeof mechanism. The code does not use heuristics, hence, no guessing games. The code takes advantage of the fact that, in general, the result type of an expression is related to one its arguments' type. For example, x + y, where x has type int and y has type double, has the result type double (the second operand type). Another example, x[y] where x is a vector<T> and y is a std::size_t, has the result type vector<T>::reference (the vector<T>'s reference type type). The limited form of type deduction presented can detect common relations if the result of a binary or unary operation, given arguments x and y with types X and Y (respectively), is X, Y, X&, Y&, X*, Y*, X const*, Y const*, bool, int, unsigned, double, container and iterator elements (e.g the T, where X is: T[N], T*, vector<T>, map<T>, vector<T>::iterator). More arguments/return type relationships can be established if needed. A set of overloaded test(T) functions capture these argument related types. Each test(T) function returns a distinct type that can be used to determine the exact type of an expression. Consider: template <typename X, typename Y> x_value_type test(X const&); template <typename X, typename Y> y_value_type test(Y const&); Given an expression x + y, where x is int and y is double, the call to: test<int, double>(x + y) will return a y_value_type. Now, if we rig x_value_type and y_value_type such that both have unique sizes, we can use sizeof(test<X, Y>(x + y)) to determine if the result type is either X or Y. For example, if: sizeof(test<X, Y>(x + y)) == sizeof(y_value_type) then, we know for sure that the result of x + y has type Y. The same basic scheme can be used to detect more argument-dependent return types where the sizeof the test(T) return type is used to index through a boost::mpl vector which holds each of the corresponding result types. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

"Joel de Guzman" <joel@boost-consulting.com> wrote in message news:403D5BCA.9080300@boost-consulting.com...
If you want a newer technology, there's one in the boost-sandbox. See: boost/utility/type_deduction.hpp and a test program: libs/utility/type_deduction_tests.cpp.
Yep, this is another solution for part I (though the one from BLL looks sufficient as well). Do you address part II (generic type-promoting operators for existing templatized types) anywhere in your libraries which are not yet part of the Boost distribution? ...Max...

Joel de Guzman wrote:
Max Motovilov wrote:
"Brian McNamara" <lorgon@cc.gatech.edu> wrote in message news:20040225235607.GA1327@gaia3.cc.gatech.edu...
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.
Looks exactly like what I was proposing, even the syntax is similar. I knew it had to be there somewhere :)
If you want a newer technology, there's one in the boost-sandbox. See: boost/utility/type_deduction.hpp and a test program: libs/utility/type_deduction_tests.cpp.
[snip] Here it is, for your perusal: http://tinyurl.com/yre5d and the test driver code: http://tinyurl.com/yqh2e This will be the basis of the LL/Phoenix merger that's being developed right now. Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

"Joel de Guzman" <joel@boost-consulting.com> wrote in message news:403D6083.6070206@boost-consulting.com...
If you want a newer technology, there's one in the boost-sandbox. See: boost/utility/type_deduction.hpp and a test program: libs/utility/type_deduction_tests.cpp.
Joel, thanks for posting the direct URLs, as I don't have access to sandbox. I tried it out - works like a charm, and compiles much faster than BLL (well, obviously....). FWIW, here's what the "universal operator+" becomes then: template< typename T1, typename T2, template< typename _ > class DAT > inline typename boost::disable_if_c< do_not< promote_sum< DAT > >::value, DAT< typename boost::result_of_plus<T1,T2>::type >
::type operator+( const DAT<T1>& a, const DAT<T2>& b ) { typedef DAT< typename boost::result_of_plus<T1,T2>::type > T; return T( a ) + T( b ); }
(promote_sum<> and do_not<> stay unchanged). ...Max...

On Wed, Feb 25, 2004 at 09:18:08PM -0600, Max Motovilov wrote:
(well, obviously....). FWIW, here's what the "universal operator+" becomes then:
template< typename T1, typename T2, template< typename _ > class DAT > inline typename boost::disable_if_c< do_not< promote_sum< DAT > >::value, DAT< typename boost::result_of_plus<T1,T2>::type >
::type operator+( const DAT<T1>& a, const DAT<T2>& b ) { typedef DAT< typename boost::result_of_plus<T1,T2>::type > T; return T( a ) + T( b ); }
Is there a way to avoid template<... template< typename _ > class DAT > ? While I don't know a class like this offhand, I can imagine a class that's typically instantiated along the lines of SomeClass<Foo,float> SomeClass<Foo,double> SomeClass<Bar,float> SomeClass<Bar,double> and it would be swell if the "DAT" technique generalized so that you could specify that you wanted to DAT-ify SomeClass<Foo,_1> I haven't thought about it hard enough, but it seems like something along the lines of changing it to template <class X, class Y> // enable_if both X and Y "isa" DAT (of the same kind) // based on specialization of result_xxx traits, deduce what was // called T1/T2 before operator+( const X& x, const Y& y ) { ... } My hunch is that it's possible, but I'm not totally sure. -- -Brian McNamara (lorgon@cc.gatech.edu)

"Brian McNamara" <lorgon@cc.gatech.edu> wrote in message news:20040226082809.GC3173@lennon.cc.gatech.edu...
Is there a way to avoid
template<... template< typename _ > class DAT >
? While I don't know a class like this offhand, I can imagine a class that's typically instantiated along the lines of
SomeClass<Foo,float> SomeClass<Foo,double> SomeClass<Bar,float> SomeClass<Bar,double>
and it would be swell if the "DAT" technique generalized so that you could specify that you wanted to DAT-ify
SomeClass<Foo,_1>
I believe I understand your intent. My intent though was to simplify defining arithmetics for types that ARE relatively straightforward templates and do not involve FP concepts at all in their definition. It is probably possible to do both using the same or similar mechanism. I'm not sure if I like the idea of having to refactor DAT<T> to something else first, even if it were done using a well-established library such as MPL. Upon reflection (forced keyboard withdrawal does wonders ;) ) the value of what you're proposing started to dawn on me. Let's say we have a policy-based DAT, Foo< T, Policy >. It still behaves as a DAT in respect to its first template parameter but it will not match the "temlpate< typename _
class" pattern. Were it re-factored as Wrap< Foo, _1, Policy > it might be easier to fit into a common infrastructure.
The problem though ts that there doesn't seem to be a way to match "template< typename _1, ... typename _N > class" - type arguments against a single template parameter. So we'll have to end up with your scheme:
template <class X, class Y> // enable_if both X and Y "isa" DAT (of the same kind) // based on specialization of result_xxx traits, deduce what was // called T1/T2 before operator+( const X& x, const Y& y ) { ... }
along with an extensive library of facilities providing "trait definition by empty template specialization" (hey, another weird acronim is coming up...) Which might not be so bad after all, except for the fact that the operators we inject are now as generic as can be (mine were at least limited to SomeTemplate<T> arguments), with only SFINAE to protect us from mayhem... Oh well, if only someone came up with the idea how NOT to put those operators into global namespace by virtue of a header file inclusion... Regards, ...Max...

On Thu, Feb 26, 2004 at 09:33:29AM -0600, Max Motovilov wrote:
Which might not be so bad after all, except for the fact that the operators we inject are now as generic as can be (mine were at least limited to SomeTemplate<T> arguments), with only SFINAE to protect us from mayhem...
IMO, you are either going to rely on SFINAE or you are not. In this case, it seems clear that you need enable_if to make the implementation work. So use it, trust in it, and let broken compilers be darned! -- -Brian McNamara (lorgon@cc.gatech.edu)

"Brian McNamara" <lorgon@cc.gatech.edu> wrote in message news:20040226202020.GB14909@lennon.cc.gatech.edu...
On Thu, Feb 26, 2004 at 09:33:29AM -0600, Max Motovilov wrote:
Which might not be so bad after all, except for the fact that the operators we inject are now as generic as can be (mine were at least limited to SomeTemplate<T> arguments), with only SFINAE to protect us from mayhem...
IMO, you are either going to rely on SFINAE or you are not. In this case, it seems clear that you need enable_if to make the implementation work. So use it, trust in it, and let broken compilers be darned!
Yes, my sentiments exactly. But I would use stronger language ;-) Jonathan

"Brian McNamara" <lorgon@cc.gatech.edu> wrote in message news:20040225235607.GA1327@gaia3.cc.gatech.edu...
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.
With Brian's suggestion in mind, the first part is gone, and the relevant code for the second part becomes: ============== using namespace boost::lambda; // Just to make the following easier to read template< template< typename _ > class DAT > struct promote_sum { typedef void tag; }; template< typename P, typename E=void > struct do_not { enum { value = false }; }; template< typename P > struct do_not< P, typename P::tag > { enum { value = true }; }; template< typename T1, typename T2, template< typename _ > class DAT > inline typename boost::disable_if_c< do_not< promote_sum< DAT > >::value, DAT< typename plain_return_type_2< arithmetic_action<plus_action>, T1,T2 >::type >
::type operator+( const DAT<T1>& a, const DAT<T2>& b ) { typedef DAT< typename plain_return_type_2< arithmetic_action<plus_action>, T1,T2 >::type > T;
return T( a ) + T( b ); } ============== The usage is not changed. The code still compiles by MSVC++ 7.1 though takes markedly longer :( (must be due a lot of declarations included from <boost/lambda/lambda.hpp>). So we reuse the mechanism, but cause a heavyweight dependency.... Since the BLL's tagging facility for aritmetic operators is so extensive, perhaps a better idea would be to parameterize promote***<> instead of using separate versions (promote_sum<>) for each operator? I.e. template <> struct promote_type< plus_action, MyDAT > {}; ...Max...
participants (4)
-
Brian McNamara
-
Joel de Guzman
-
Jonathan Turkanis
-
Max Motovilov