review request: addition to type_traits library of is_less_comparable<T, U> and others

I would like to propose to your review the following addition to the type_traits library (at https://svn.boost.org/trac/boost/browser/sandbox/type_traits and http://dl.free.fr/tK0BwIzYy). The purpose of the addition is to add type traits to detect if types T and U are comparable in the sens of <, <=, >, >=, == or != operators, i.e. if t<u has a sens when t is of type T and u of type U (same for <=, >, >=, ==, !=). The following traits are added: is_equal_to_comparable<T,U> is_greater_comparable<T,U> is_greater_equal_comparable<T,U> is_less_comparable<T,U> is_less_equal_comparable<T,U> is_not_equal_to_comparable<T,U> The names are based on the corresponding names of the standard template library (<functional> header, section 20.3.3 of the standard). The code has the following properties: * returns true if t<u is meaningful and returns a value convertible to bool * returns false if t<u is meaningless. * fails with compile time error if t<u is meaningful and returns void (a possibility to avoid compile time error would be to return true with an operator, trick but this has little sens as returning false would be better) The compilation has been tested with g++ 4.3.2 and icpc 10.0 on linux 64 bits, g++ 3.4.4 on cygwin, mingw32 3.4.4 on cygwin. Regards, Frédéric

Frédéric Bron wrote:
I would like to propose to your review the following addition to the type_traits library (at https://svn.boost.org/trac/boost/browser/sandbox/type_traits and http://dl.free.fr/tK0BwIzYy).
The purpose of the addition is to add type traits to detect if types T and U are comparable in the sens of <, <=, >, >=, == or != operators, i.e. if t<u has a sens when t is of type T and u of type U (same for <=, >, >=, ==, !=).
The following traits are added:
is_equal_to_comparable<T,U> is_greater_comparable<T,U> is_greater_equal_comparable<T,U> is_less_comparable<T,U> is_less_equal_comparable<T,U> is_not_equal_to_comparable<T,U>
The names are based on the corresponding names of the standard template library (<functional> header, section 20.3.3 of the standard).
The code has the following properties: * returns true if t<u is meaningful and returns a value convertible to bool * returns false if t<u is meaningless. * fails with compile time error if t<u is meaningful and returns void (a possibility to avoid compile time error would be to return true with an operator, trick but this has little sens as returning false would be better)
Is it reasonable to additionally verify the returned type? IMO, such kind of traits should not make assumptions about the operators meaning, they should merely show whether the comparison expression is valid. Thus there also should not be a fails-to-compile case.

Andrey Semashev wrote:
Frédéric Bron wrote:
I would like to propose to your review the following addition to the type_traits library (at https://svn.boost.org/trac/boost/browser/sandbox/type_traits and http://dl.free.fr/tK0BwIzYy).
Is it reasonable to additionally verify the returned type? IMO, such kind of traits should not make assumptions about the operators meaning, they should merely show whether the comparison expression is valid. Thus there also should not be a fails-to-compile case.
+1; perhaps there should be a 3rd template parameter, defaulted to bool to preserve the current semantics, which the user may specify as void to ignore the return type...? - Jeff

Is it reasonable to additionally verify the returned type? IMO, such kind of traits should not make assumptions about the operators meaning, they should merely show whether the comparison expression is valid. Thus there also should not be a fails-to-compile case.
OK, what do you know if you know that you can write t<u but do not know if it is convertible to bool? You know that "t<u ;" is OK but maybe "if (t<u) ..." will not work -> compile time error. I think that in most cases you want to know that it behaves like operator< used with built-in types. Maybe it could be interesting for some people to have an additionnal version just checking for existence but I am not convinced. I agree that it is not "nice" to have a compile time error when used with operator< returning void but I prefer that than returning true because this behaviour is not the one expected by operator<. If anybody has a solution on how to return false in such case... Frédéric

Frédéric Bron wrote:
Maybe it could be interesting for some people to have an additionnal version just checking for existence but I am not convinced.
what about: is_less_than_comparable and has_operator_less_than first check for existance and bool compliance, second check for existence only -- ___________________________________________ Joel Falcou - Assistant Professor PARALL Team - LRI - Universite Paris Sud XI Tel : (+33)1 69 15 66 35

Frédéric Bron wrote:
what about: is_less_than_comparable and has_operator_less_than first check for existance and bool compliance, second check for existence only
Sounds very good to me. has_operator_less_than or has_less_than_operator?
I agree that splitting those is useful. has_operator_less_than is more consistent with C++ syntax: operator <. If we (can) add functionality to deduce the operator's return type, then I think you'd need something like result_of_operator_less_than<T,U>::type. Given that, one can check it against void, bool, tribool, or anything else of interest. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

AMDG Stewart, Robert wrote:
Frédéric Bron wrote:
what about: is_less_than_comparable and has_operator_less_than first check for existance and bool compliance, second check for existence only
Sounds very good to me. has_operator_less_than or has_less_than_operator?
I agree that splitting those is useful. has_operator_less_than is more consistent with C++ syntax: operator <.
If we (can) add functionality to deduce the operator's return type, then I think you'd need something like result_of_operator_less_than<T,U>::type. Given that, one can check it against void, bool, tribool, or anything else of interest.
It's easier to test the result of operator< (for example to determine whether it is convertible to bool than it is to deduce the return type.) In Christ, Steven Watanabe

joel wrote:
Frédéric Bron wrote:
Maybe it could be interesting for some people to have an additionnal version just checking for existence but I am not convinced.
what about:
is_less_than_comparable and has_operator_less_than
first check for existance and bool compliance, second check for existence only
That would make me happy. :)

Frédéric Bron wrote:
Is it reasonable to additionally verify the returned type? IMO, such kind of traits should not make assumptions about the operators meaning, they should merely show whether the comparison expression is valid. Thus there also should not be a fails-to-compile case.
OK, what do you know if you know that you can write t<u but do not know if it is convertible to bool? You know that "t<u ;" is OK but maybe "if (t<u) ..." will not work -> compile time error. I think that in most cases you want to know that it behaves like operator< used with built-in types.
I think the tribool example -- and other UDTs with Boolean-plus behavior -- is a great example for not assuming convertible-to-bool is the only behavior to support.
I agree that it is not "nice" to have a compile time error when used with operator< returning void but I prefer that than returning true because this behaviour is not the one expected by operator<. If anybody has a solution on how to return false in such case...
It may be that being able to examine the return type -- we never did anything to make that work, did we? -- of the various operators would leave the answer to that question to the caller. For example, if < returns void, but > returns something convertible to bool, generic code could use > instead of < for sorting. (Far fetched, to be sure, but the idea, in general, seems reasonable.) I also agree with the idea of providing a "normal" case of "is t < u usable in a Boolean context?" _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

It may be that being able to examine the return type -- we never did anything to make that work, did we? -- of the various operators would leave the answer to that question to the caller. For example, if < returns void, but > returns something convertible to bool, generic code could use > instead of < for sorting. (Far fetched, to be sure, but the idea, in general, seems reasonable.)
We had a bit of discussion before that thread and never achieved to get the exact result type of an operator. The only thing we could do is know if the result type is convertible to something. If somebody has a code to get the result type of some function, it would be very interesting to add that traits alone... Frédéric

I have started to modify the code so that the return type of operator< can be specified instead of checking only for convertible to bool. For that to work, I need a small addition to detail/bool_trait_def.hpp. John, can I commit the following patch to the sandbox? Frédéric Index: boost/type_traits/detail/bool_trait_def.hpp =================================================================== --- boost/type_traits/detail/bool_trait_def.hpp (révision 56930) +++ boost/type_traits/detail/bool_trait_def.hpp (copie de travail) @@ -87,6 +87,17 @@ BOOST_TT_AUX_TEMPLATE_ARITY_SPEC(2,trait) \ /**/ +#define BOOST_TT_AUX_BOOL_TRAIT_DEF3(trait,T1,T2,T3,C) \ +template< typename T1, typename T2, typename T3 > struct trait \ + BOOST_TT_AUX_BOOL_C_BASE(C) \ +{ \ + BOOST_TT_AUX_BOOL_TRAIT_VALUE_DECL(C) \ + BOOST_MPL_AUX_LAMBDA_SUPPORT(3,trait,(T1,T2,T3)) \ +}; \ +\ +BOOST_TT_AUX_TEMPLATE_ARITY_SPEC(3,trait) \ +/**/ + #define BOOST_TT_AUX_BOOL_TRAIT_SPEC1(trait,sp,C) \ template<> struct trait< sp > \ BOOST_TT_AUX_BOOL_C_BASE(C) \

Frédéric Bron wrote:
Is it reasonable to additionally verify the returned type? IMO, such kind of traits should not make assumptions about the operators meaning, they should merely show whether the comparison expression is valid. Thus there also should not be a fails-to-compile case.
OK, what do you know if you know that you can write t<u but do not know if it is convertible to bool? You know that "t<u ;" is OK but maybe "if (t<u) ..." will not work -> compile time error. I think that in most cases you want to know that it behaves like operator< used with built-in types.
Knowing that an expression is valid and that the expression can be used in a specific context are different things. Therefore I would like to see the tools that provide these two kinds of knowledge decoupled. Otherwise you make these traits bound to a single specific use case, no matter how widely used it is.
Maybe it could be interesting for some people to have an additionnal version just checking for existence but I am not convinced.
I agree that it is not "nice" to have a compile time error when used with operator< returning void but I prefer that than returning true because this behaviour is not the one expected by operator<. If anybody has a solution on how to return false in such case...
You can achieve that with typeof/decltype. Something like that (not tested): #include <boost/type.hpp> namespace adl_block { struct something_else {}; struct convertible_from_any { template< typename T > convertible_from_any(T const&); }; something_else operator< ( convertible_from_any, convertible_from_any); template< typename T, typename U > struct is_less_comparable_impl { typedef char yes_type; struct no_type { char _[2]; }; template< typename V > static yes_type check(type< V > const&); static no_type check(type< something_else > const&); static T make_T(); static U make_U(); enum _ { value = sizeof(yes_type) == sizeof(check(type< decltype(make_T() < make_U()) >())) }; }; } template< typename T, typename U > struct is_less_comparable : mpl::bool_< adl_block::is_less_comparable_impl< T, U >::value > { };

I am starting again to work on adding new traits to detect if two types can be compared (is_less_comparable<T,U> and has_operator_less<T,U>). Sorry for the long break but I could not find any time for this in the past weeks. A recent post on the list makes me think that we could enlarge the scope of the addition. In fact, here is the list of binary operators for which we could implement type trait detection: * comparisons: <, <=, >, >=, ==, != * arithmetic operations: +, -, *, /, % * logical operations: && (and), || (or) * bitwise operations: <<, >>, ^ (xor), & (bitand), | (bitor) I a in favor of implementing type traits to detect comparison and arithmetic operators. What for logical and bitwise operations? Frédéric

I am starting again to work on adding new traits to detect if two types can be compared (is_less_comparable<T,U> and has_operator_less<T,U>). Sorry for the long break but I could not find any time for this in the past weeks.
A recent post on the list makes me think that we could enlarge the scope of the addition. In fact, here is the list of binary operators for which we could implement type trait detection:
* comparisons: <, <=, >, >=, ==, != * arithmetic operations: +, -, *, /, % * logical operations: && (and), || (or) * bitwise operations: <<, >>, ^ (xor), & (bitand), | (bitor)
I would like to propose the following names for the type traits based on corresponding names in header <functional>. The first proposed name for type traits checking only for existence and the second name for type traits checking for existence and standard behaviour, i.e. the operator returns non void convertible to something the user can supply as template parameter (default bool for comparison operators). For existence, I am quite happy with the names, for existence and standard behaviour, I would like something shorter... One could also be in favor of has_operator_less or has_less_operator. The 1st one is consistant with declaration operator<(...), the 2nd one is consistant with already existing type trait has_new_operator and English I suppose. operator name in <functional> proposed name (existence) proposed name (existence and non void return type convertible to something) < less has_operator_less operator_less_has_standard_behaviour <= less_equal has_operator_less_equal operator_less_equal_has_standard_behaviour
greater has_operator_greater operator_greater_has_standard_behaviour = greater_equal has_operator_greater_equal operator_greater_equal_has_standard_behaviour
== equal_to has_operator_equal_to operator_equal_to_has_standard_behaviour != not_equal_to has_operator_not_equal_to operator_not_equal_to_has_standard_behaviour && logical_and has_operator_and operator_and_has_standard_behaviour || logical_or has_operator_or operator_or_has_standard_behaviour + plus has_operator_plus operator_plus_has_standard_behaviour - minus has_operator_minus operator_minus_has_standard_behaviour * multiplies has_operator_multiplies operator_multiplies_has_standard_behaviour / divides has_operator_divides operator_divides_has_standard_behaviour % modulus has_operator_modulus operator_modulus_has_standard_behaviour Please give your feedback and do not hesitate to make other propositions. Frédéric

2009/12/7 Frédéric Bron <frederic.bron@m4x.org>
I am starting again to work on adding new traits to detect if two types can be compared (is_less_comparable<T,U> and has_operator_less<T,U>). Sorry for the long break but I could not find any time for this in the past weeks.
A recent post on the list makes me think that we could enlarge the scope of the addition. In fact, here is the list of binary operators for which we could implement type trait detection:
* comparisons: <, <=, >, >=, ==, != * arithmetic operations: +, -, *, /, % * logical operations: && (and), || (or) * bitwise operations: <<, >>, ^ (xor), & (bitand), | (bitor)
I would like to propose the following names for the type traits based on corresponding names in header <functional>. The first proposed name for type traits checking only for existence and the second name for type traits checking for existence and standard behaviour, i.e. the operator returns non void convertible to something the user can supply as template parameter (default bool for comparison operators). For existence, I am quite happy with the names, for existence and standard behaviour, I would like something shorter...
One could also be in favor of has_operator_less or has_less_operator. The 1st one is consistant with declaration operator<(...), the 2nd one is consistant with already existing type trait has_new_operator and English I suppose.
operator name in <functional> proposed name (existence) proposed name (existence and non void return type convertible to something) < less has_operator_less operator_less_has_standard_behaviour <= less_equal has_operator_less_equal operator_less_equal_has_standard_behaviour
greater has_operator_greater
operator_greater_has_standard_behaviour
= greater_equal has_operator_greater_equal operator_greater_equal_has_standard_behaviour == equal_to has_operator_equal_to operator_equal_to_has_standard_behaviour != not_equal_to has_operator_not_equal_to operator_not_equal_to_has_standard_behaviour && logical_and has_operator_and operator_and_has_standard_behaviour || logical_or has_operator_or operator_or_has_standard_behaviour
+ plus has_operator_plus operator_plus_has_standard_behaviour - minus has_operator_minus operator_minus_has_standard_behaviour * multiplies has_operator_multiplies operator_multiplies_has_standard_behaviour / divides has_operator_divides operator_divides_has_standard_behaviour % modulus has_operator_modulus operator_modulus_has_standard_behaviour
Please give your feedback and do not hesitate to make other propositions.
How about rationalising over the operators themselves? boost::operator boost::operator::plus boost::operator::plus::exists<A,B> boost::operator::plus::return_type<A,B> Christian

AMDG Christian Schladetsch wrote:
How about rationalising over the operators themselves?
boost::operator boost::operator::plus boost::operator::plus::exists<A,B> boost::operator::plus::return_type<A,B>
You'd have to use operator_ because operator is a keyword. In Christ, Steven Watanabe

How about rationalising over the operators themselves?
boost::operator boost::operator::plus boost::operator::plus::exists<A,B> boost::operator::plus::return_type<A,B>
Personally I find that really ugly, sorry :-( John.

operator name in <functional> proposed name (existence) proposed name (existence and non void return type convertible to something) < less has_operator_less operator_less_has_standard_behaviour
I don't like operator_less_has_standard_behaviour at all. In fact is there really any point to it? Why not simply: has_operator_less<T, R = bool> Where R is the expected return type. In all but a vanishingly small number of cases R will be bool. If you really want to support the "don't care" case for a return type, use has_operator_less<T, dont_care> where "dont_care" is a special placeholder type that you define (could use a better name though?).
+ plus has_operator_plus operator_plus_has_standard_behaviour
These are more complex because we often have mixed mode arithmetic, what about: has_operator_plus<T, U> ? In fact an important use case might be to work out if I can add a T to a U :-) So how about: has_operator_plus<T, U = T, R = dont_care> ? Hard to decide what R should default to in this case, I'm tempted by the "permissive" case of dont_care because it will do the right thing in more mixed arithmetic cases, but the alternative would be to default R to T I guess. Just my 2c, John.

I don't like operator_less_has_standard_behaviour at all. In fact is there really any point to it? Why not simply: has_operator_less<T, R = bool>
It would be: has_operator_less<T, U, R=bool> as we said in previous discussion we want to address the case where we compare T with U!=T.
Where R is the expected return type. In all but a vanishingly small number of cases R will be bool. If you really want to support the "don't care" case for a return type, use has_operator_less<T, dont_care> where "dont_care" is a special placeholder type that you define (could use a better name though?).
the "don't care" case was a strong requirement from previous discussion. I like your proposed solution has_operator_less<T, U, dont_care> dont_care would have to be an empty class maybe boost::type_traits::dont_care?
+ plus has_operator_plus
These are more complex because we often have mixed mode arithmetic, what about: has_operator_plus<T, U> ?
right, that is what I meant
So how about: has_operator_plus<T, U = T, R = dont_care> ?
Hard to decide what R should default to in this case, I'm tempted by the "permissive" case of dont_care because it will do the right thing in more mixed arithmetic cases, but the alternative would be to default R to T I guess.
I am in favor of dont_care Frédéric

Where R is the expected return type. In all but a vanishingly small number of cases R will be bool. If you really want to support the "don't care" case for a return type, use has_operator_less<T, dont_care> where "dont_care" is a special placeholder type that you define (could use a better name though?).
the "don't care" case was a strong requirement from previous discussion. I like your proposed solution has_operator_less<T, U, dont_care>
dont_care would have to be an empty class maybe boost::type_traits::dont_care?
Yep. John.

On Dec 7, 2009, at 2:34 AM, John Maddock wrote:
Where R is the expected return type. In all but a vanishingly small number of cases R will be bool. If you really want to support the "don't care" case for a return type, use has_operator_less<T, dont_care> where "dont_care" is a special placeholder type that you define (could use a better name though?).
the "don't care" case was a strong requirement from previous discussion. I like your proposed solution has_operator_less<T, U, dont_care>
dont_care would have to be an empty class maybe boost::type_traits::dont_care?
Yep.
Why not void? -- David Abrahams BoostPro Computing http://boostpro.com

On Mon, Dec 7, 2009 at 9:12 PM, John Maddock <john@johnmaddock.co.uk> wrote:
operator name in <functional> proposed name (existence)
proposed name (existence and non void return type convertible to something) < less has_operator_less operator_less_has_standard_behaviour
I don't like operator_less_has_standard_behaviour at all. In fact is there really any point to it?
Why not simply:
has_operator_less<T, R = bool>
Where R is the expected return type. In all but a vanishingly small number of cases R will be bool. If you really want to support the "don't care" case for a return type, use has_operator_less<T, dont_care> where "dont_care" is a special placeholder type that you define (could use a better name though?).
Perhaps boost::has_operator_less<T,R=bool> Is "less ugly" than boost::operators::less::exists<T, R=bool> But is it 'better'? Using boost::operators::[name] has enough potential benefits that I don't see why it should be rejected so casually: * boost::operators::[tab]::[tab] works better with Intellisense * it is factored over the operators, which provides a common structure for all operators in a tree * it provides a single type that designates each operation uniquely. that is, operators can become first-class objects. Christian

Christian Schladetsch wrote:
On Mon, Dec 7, 2009 at 9:12 PM, John Maddock <john@johnmaddock.co.uk> wrote:
operator name in <functional> proposed name (existence)
proposed name (existence and non void return type convertible to something) < less has_operator_less operator_less_has_standard_behaviour
If there were reason to keep it, I'd suggest something more like "std_operator_less," but that suggests a Standard-mandated interface, which there isn't (for UDTs).
I don't like operator_less_has_standard_behaviour at all. In fact is there really any point to it?
Why not simply:
has_operator_less<T, R = bool>
has_operator_less<T, U, R = bool> is nice.
Where R is the expected return type. In all but a vanishingly small number of cases R will be bool.
Agreed.
If you really want to support the "don't care" case for a return type, use has_operator_less<T, dont_care> where "dont_care" is a special placeholder type that you define (could use a better name though?).
I like the idea and the name, though perhaps "any" would work, too.
Perhaps
boost::has_operator_less<T,R=bool>
Is "less ugly" than
boost::operators::less::exists<T, R=bool>
But is it 'better'?
It is more in keeping with the existing traits, so if the latter were offered, the former would still be appropriate.
Using boost::operators::[name] has enough potential benefits that I don't see why it should be rejected so casually:
* boost::operators::[tab]::[tab] works better with Intellisense
I'm sure that holds sway with many, but I don't care about it.
* it is factored over the operators, which provides a common structure for all operators in a tree
The structure is nice, but "has_operator_<op>" with a return type template argument also provides structure.
* it provides a single type that designates each operation uniquely. that is, operators can become first-class objects.
There may be significant value on this point. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

If you really want to support the "don't care" case for a return type, use has_operator_less<T, dont_care> where "dont_care" is a special placeholder type that you define (could use a better name though?).
I like the idea and the name, though perhaps "any" would work, too.
Doesn't that potentially get confused with boost::any ? John.

If you really want to support the "don't care" case for a return type, use has_operator_less<T, dont_care> where "dont_care" is a special placeholder type that you define (could use a better name though?).
I like the idea and the name, though perhaps "any" would work, too.
Doesn't that potentially get confused with boost::any ?
It would be has_operator<T, U, boost::type_traits::any> Then boost::type_traits::any_return would be more appropriate. I prefer void because it is shorter but this could make people think it will check for operator return void... which is not the case; so maybe the long version boost::type_traits::any_return is better. Frédéric

On Dec 8, 2009, at 6:28 AM, Frédéric Bron wrote:
If you really want to support the "don't care" case for a return type, use has_operator_less<T, dont_care> where "dont_care" is a special placeholder type that you define (could use a better name though?).
I like the idea and the name, though perhaps "any" would work, too.
Doesn't that potentially get confused with boost::any ?
It would be has_operator<T, U, boost::type_traits::any> Then boost::type_traits::any_return would be more appropriate.
I prefer void because it is shorter but this could make people think it will check for operator return void... which is not the case; so maybe the long version boost::type_traits::any_return is better.
By that rationale shouldn't we also require the 3rd parameter to be wrapped in is_convertible_to<...> so people don't think it's requiring a return type of exactly R (e.g. bool)? -- David Abrahams BoostPro Computing http://boostpro.com

David Abrahams wrote:
On Dec 8, 2009, at 6:28 AM, Frédéric Bron wrote:
I prefer void because it is shorter but this could make people think it will check for operator return void... which is not the case; so maybe the long version boost::type_traits::any_return is better.
By that rationale shouldn't we also require the 3rd parameter to be wrapped in is_convertible_to<...> so people don't think it's requiring a return type of exactly R (e.g. bool)?
Would this also allow a user to override this behavior when they want to ensure an exact type match? --Jeffrey Bosboom

AMDG Frédéric Bron wrote:
Would this also allow a user to override this behavior when they want to ensure an exact type match?
From earlier discussion, we conclude it was not possible to check for an exact return type, only convertible to is achievable.
It is (almost) possible to check the exact return type. template<class Expected, class Actual> boost::is_same<Expected, Actual> test(const Actual&); Note that this doesn't handle references correctly. In Christ, Steven Watanabe

It is (almost) possible to check the exact return type.
template<class Expected, class Actual> boost::is_same<Expected, Actual> test(const Actual&);
Does not work with void return operator: #include <iostream> #include <boost/typeof/typeof.hpp> #include <boost/type_traits/is_same.hpp> template<class Expected, class Actual> boost::is_same<Expected, Actual> test(const Actual&){}; bool f(){} void g(){} int main() { typedef BOOST_TYPEOF(test<bool>(f())) type_f; std::cout << "f:" << type_f::value << std::endl; typedef BOOST_TYPEOF(test<bool>(g())) type_g; std::cout << "g:" << type_g::value << std::endl; return 0; }

I have found another issue with these traits: it will not be possible to treat all combinations of built-in types that do not work with operators. Example: 1. % 1. is not allowed by the standard and cannot be redefined. Even the trick of an "any" class does not work. See below. There is still the possibility to specialize the traits for all built-in non working combinations... Frédéric #include <iostream> template <class T> struct any { any(const T&) { }; }; template <class T> double operator % (any<T> x, any<T> y) { std::cout << "my operator %\n" ; return 0.; } int main() { double x=1.; x=x%x; // compile time error -> error: invalid operands of types double and double to binary operator% x=any<double>(x)%any<double>(x); return 0.; }

AMDG Frédéric Bron wrote:
It is (almost) possible to check the exact return type.
template<class Expected, class Actual> boost::is_same<Expected, Actual> test(const Actual&);
Does not work with void return operator:
Of course it doesn't work as is. It would have to be added as an additional check after you already know that the return type is non-void. In Christ, Steven Watanabe

It is (almost) possible to check the exact return type.
template<class Expected, class Actual> boost::is_same<Expected, Actual> test(const Actual&);
Does not work with void return operator:
Of course it doesn't work as is. It would have to be added as an additional check after you already know that the return type is non-void.
But what do we really want? (i) check for convertible to something (default bool) or (ii) check for exact return type? For sure we need (i). Do we need (ii) and if yes, it would have to be another traits? Frédéric

AMDG Frédéric Bron wrote:
It is (almost) possible to check the exact return type.
template<class Expected, class Actual> boost::is_same<Expected, Actual> test(const Actual&);
Does not work with void return operator:
Of course it doesn't work as is. It would have to be added as an additional check after you already know that the return type is non-void.
But what do we really want? (i) check for convertible to something (default bool) or (ii) check for exact return type? For sure we need (i). Do we need (ii) and if yes, it would have to be another traits?
We probably don't need (ii). I was just pointing out that it was /possible/. In Christ, Steven Watanabe

On Dec 8, 2009, at 7:06 AM, Jeffrey Bosboom wrote:
David Abrahams wrote:
On Dec 8, 2009, at 6:28 AM, Frédéric Bron wrote:
I prefer void because it is shorter but this could make people think it will check for operator return void... which is not the case; so maybe the long version boost::type_traits::any_return is better. By that rationale shouldn't we also require the 3rd parameter to be wrapped in is_convertible_to<...> so people don't think it's requiring a return type of exactly R (e.g. bool)?
Would this also allow a user to override this behavior when they want to ensure an exact type match?
I suppose so, but seriously, I was not suggesting adding all this complication. I think it should be kept simple and only generalized as far as is proven necessary by real use cases. -- David Abrahams BoostPro Computing http://boostpro.com

David Abrahams wrote:
On Dec 8, 2009, at 7:06 AM, Jeffrey Bosboom wrote:
David Abrahams wrote:
On Dec 8, 2009, at 6:28 AM, Frédéric Bron wrote:
I prefer void because it is shorter but this could make people think it will check for operator return void... which is not the case; so maybe the long version boost::type_traits::any_return is better. By that rationale shouldn't we also require the 3rd parameter to be wrapped in is_convertible_to<...> so people don't think it's requiring a return type of exactly R (e.g. bool)? Would this also allow a user to override this behavior when they want to ensure an exact type match?
I suppose so, but seriously, I was not suggesting adding all this complication. I think it should be kept simple and only generalized as far as is proven necessary by real use cases.
I don't have a personal need for this, but I was thinking that it might be useful in generic code that can use multiple number types. Suppose you have a some_number class that has a converting constructor from long. Your generic code might want to ensure that if you add two some_numbers (a + b) that you get back a some_number, not a long that's convertible to a some_number. I'm not sure this is entirely necessary, and there may be other mechanisms for the user to specify this constraint when it is required. --Jeffrey Bosboom

David Abrahams wrote:
On Dec 8, 2009, at 6:28 AM, Frédéric Bron wrote:
John Maddock wrote:
Rob Stewart wrote:
Frédéric Bron wrote:
If you really want to support the "don't care" case for a return type, use has_operator_less<T, dont_care> where "dont_care" is a special placeholder type that you define (could use a better name though?).
I like the idea and the name, though perhaps "any" would work, too.
Doesn't that potentially get confused with boost::any ?
Eh, maybe, but I don't think so. The idea is similar -- any type -- and the documentation will clearly indicate what "any" means in this case. As Frédéric wrote, the test is always for is_convertible_to, not an exact match, so I think the name fits well.
It would be has_operator<T, U, boost::type_traits::any> Then boost::type_traits::any_return would be more appropriate.
That is clear, doesn't conflict with boost::any, but also seems overly long. How about "whatever!" :0) Seriously, "anything" might be a good compromise.
I prefer void because it is shorter but this could make people think it will check for operator return void... which is not the case; so maybe the long version boost::type_traits::any_return is better.
By that rationale shouldn't we also require the 3rd parameter to be wrapped in is_convertible_to<...> so people don't think it's requiring a return type of exactly R (e.g. bool)?
Since is_convertible_to is always implied, and any type can be converted -- using the term loosely -- to no type, "void" might not be confusing. The name used in this context could be left to reviewers to choose. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Since is_convertible_to is always implied, and any type can be converted -- using the term loosely -- to no type, "void" might not be confusing. The name used in this context could be left to reviewers to choose.
how does is_convertible_to<> handle void? ie are all types considered convertible to void? Tony

John Maddock wrote:
has_operator_less<T, R = bool>
Maybe should be has_operator_less< T, U = T, R = bool > ? (one step further than frederic's suggestion, and in line with your has_operator_plus suggestions below)
So how about: has_operator_plus<T, U = T, R = dont_care> ?
Hard to decide what R should default to in this case, I'm tempted by the "permissive" case of dont_care because it will do the right thing in more mixed arithmetic cases, but the alternative would be to default R to T I guess.
Default R = common_type<T,U>::type (the common_type from another thread) ? And (somewhat off-topic) I think the default common_type should *not* use decltype (i.e., Boost.Typeof) if not available, which would limit common_type<T,U>::type to be either T or U most of the time, unless specialized. Also, why couldn't void take the place of dont_care ?
Just my 2c, John.
And that's mine. - Jeff

has_operator_less<T, R = bool>
Maybe should be has_operator_less< T, U = T, R = bool > ? (one step further than frederic's suggestion, and in line with your has_operator_plus suggestions below)
So how about: has_operator_plus<T, U = T, R = dont_care> ?
Hard to decide what R should default to in this case, I'm tempted by the "permissive" case of dont_care because it will do the right thing in more mixed arithmetic cases, but the alternative would be to default R to T I guess.
Default R = common_type<T,U>::type (the common_type from another thread) ?
Doesn't this put the cart before the horse? If there is no common type because the no such operator is defined won't we get a compile error rather than the false result that we want? Plus common_type would rely on typeof-emulation and type registration, which rather negates the point of defining the type_traits class?
And (somewhat off-topic) I think the default common_type should *not* use decltype (i.e., Boost.Typeof) if not available, which would limit common_type<T,U>::type to be either T or U most of the time, unless specialized.
Ah I see, it would have to in order to work in this case, but might give the wrong answer in other cases?
Also, why couldn't void take the place of dont_care ?
Depends on whether we want to differentiate between operators that return void, and the genuine "don't care" case? But... I suspect that detecting a void return isn't possible anyway, so yes maybe that would work. Cheers, John.

On Dec 6, 2009, at 5:17 AM, Frédéric Bron wrote:
The first proposed name for type traits checking only for existence and the second name for type traits checking for existence and standard behaviour, i.e. the operator returns non void convertible to something the user can supply as template parameter (default bool for comparison operators).
Nit: structural/syntactic elements like the function's return type should not be confused with behavioral/semantic ones. -- David Abrahams BoostPro Computing http://boostpro.com

Frédéric Bron wrote:
I a in favor of implementing type traits to detect comparison and arithmetic operators. What for logical and bitwise operations? I have no particular use cases in mind, but my prediction is that if you only do the comparison and arithmetic operators, later someone will be saying, "Why didn't he do that logical and bitwise operators? Now I have to implement them myself!"
Patrick

On Sun, Dec 6, 2009 at 11:19 AM, Patrick Horgan <phorgan1@gmail.com> wrote:
Frédéric Bron wrote:
I a in favor of implementing type traits to detect comparison and arithmetic operators. What for logical and bitwise operations?
I have no particular use cases in mind, but my prediction is that if you only do the comparison and arithmetic operators, later someone will be saying, "Why didn't he do that logical and bitwise operators? Now I have to implement them myself!"
A use case I came up with recently that might warrant the need for virtually all operators was implementing a scripting system with a limited number of types. script variables were represented by a boost::variant<> templatized on the set of all possible c++ types that mapped to the script types. Some of these types had various operations possible between them (int+float, string+string, etc) and some combinations didn't (string+int, double-string, etc). I wanted to use the static_visitor pattern and have it throw an exception if the operation did not exist between the two. So I wrote functors such as add_variables() and divide_variables() that used enable_if<> and disable_if<> on the operator() to select the appropriate overload for the visitor to invoke. Since this is a scripting language, there were many more operations possible, not just arithmetic. Zach

On Dec 6, 2009, at 9:19 AM, Patrick Horgan wrote:
Frédéric Bron wrote:
I a in favor of implementing type traits to detect comparison and arithmetic operators. What for logical and bitwise operations? I have no particular use cases in mind, but my prediction is that if you only do the comparison and arithmetic operators, later someone will be saying, "Why didn't he do that logical and bitwise operators? Now I have to implement them myself!"
I hope if you do this there will be a macro that generates the trait for a given operator, at least in the implementation. Once we have that there should be no reason not to cover all the operators. -- David Abrahams BoostPro Computing http://boostpro.com

Hi Frederic, I have received your request and added your library to the Boost formal review schedule. Best, Ron On Oct 8, 2009, at 12:44 AM, Frédéric Bron wrote:
I would like to propose to your review the following addition to the type_traits library (at https://svn.boost.org/trac/boost/browser/sandbox/type_traits and http://dl.free.fr/tK0BwIzYy).
The purpose of the addition is to add type traits to detect if types T and U are comparable in the sens of <, <=, >, >=, == or != operators, i.e. if t<u has a sens when t is of type T and u of type U (same for <=, >,
=, ==, !=).
The following traits are added:
is_equal_to_comparable<T,U> is_greater_comparable<T,U> is_greater_equal_comparable<T,U> is_less_comparable<T,U> is_less_equal_comparable<T,U> is_not_equal_to_comparable<T,U>
The names are based on the corresponding names of the standard template library (<functional> header, section 20.3.3 of the standard).
The code has the following properties: * returns true if t<u is meaningful and returns a value convertible to bool * returns false if t<u is meaningless. * fails with compile time error if t<u is meaningful and returns void (a possibility to avoid compile time error would be to return true with an operator, trick but this has little sens as returning false would be better)
The compilation has been tested with g++ 4.3.2 and icpc 10.0 on linux 64 bits, g++ 3.4.4 on cygwin, mingw32 3.4.4 on cygwin.
Regards,
Frédéric _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Thu, 8 Oct 2009 08:44:09 +0200 Frédéric Bron <frederic.bron@m4x.org> wrote:
I would like to propose to your review the following addition to the type_traits library (at https://svn.boost.org/trac/boost/browser/sandbox/type_traits and http://dl.free.fr/tK0BwIzYy).
The purpose of the addition is to add type traits to detect if types T and U are comparable in the sens of <, <=, >, >=, == or != operators, i.e. if t<u has a sens when t is of type T and u of type U (same for <=, >, >=, ==, !=).
The following traits are added:
is_equal_to_comparable<T,U> is_greater_comparable<T,U> is_greater_equal_comparable<T,U> is_less_comparable<T,U> is_less_equal_comparable<T,U> is_not_equal_to_comparable<T,U>
The names are based on the corresponding names of the standard template library (<functional> header, section 20.3.3 of the standard).
The code has the following properties: * returns true if t<u is meaningful and returns a value convertible to bool * returns false if t<u is meaningless. * fails with compile time error if t<u is meaningful and returns void (a possibility to avoid compile time error would be to return true with an operator, trick but this has little sens as returning false would be better)
The compilation has been tested with g++ 4.3.2 and icpc 10.0 on linux 64 bits, g++ 3.4.4 on cygwin, mingw32 3.4.4 on cygwin.
Regards,
Frédéric
Is there some practical advantage to having type traits for all of these operators? "<=, >, >=, ==, and !=" can all be expressed in terms of "<". It seems like if all of the operators are supported it will encourage boost developers to move complexity from the back end (what boost developers see) to the front end (what users see). If a boost developer requires a user defined type to specify more than "<" for the purpose of doing any of "<, <=, >, >=, ==, and !=", it effectively moves complexity to the front end. -Seth

Seth Bunce wrote:
Is there some practical advantage to having type traits for all of these operators?
"<=, >, >=, ==, and !=" can all be expressed in terms of "<".
IIRC, there was already a short discussion on this recently (also in the context of this library), with the conclusion being that this is not always the case. You might want to search the archives...? What immediately comes to my mind are expression templates (e.g., Boost.Proto). This is also a case where the return type is not necessarily convertible to bool. I don't think this library should decide whether it's appropriate for a client of this library to test operator< for such types; leave that decision for the client. Also, it makes sense for some types to return, e.g., a boost::tribool; Boost.Interval is such an example. Though convertible to bool, a user may be interested that the result of operator< is, specfically, a boost::tribool, and, e.g., throw if the result is indeterminate.
It seems like if all of the operators are supported it will encourage boost developers to move complexity from the back end (what boost developers see) to the front end (what users see).
If a boost developer requires a user defined type to specify more than "<" for the purpose of doing any of "<, <=, >, >=, ==, and !=", it effectively moves complexity to the front end.
-Seth
Are you saying that boost's current quality controls are insufficient to catch unnecessary developer-imposed requirements on the client? ;) To reiterate, I didn't get the sense that the scope of this library was to dictate semantics. It seems more like a syntactic querying tool. I may have misinterpreted Frederic's and the library's intents, though ;) - Jeff

On Thu, 08 Oct 2009 20:38:24 -0700 Jeffrey Hellrung <jhellrung@ucla.edu> wrote:
What immediately comes to my mind are expression templates (e.g., Boost.Proto). This is also a case where the return type is not necessarily convertible to bool. I don't think this library should decide whether it's appropriate for a client of this library to test operator< for such types; leave that decision for the client.
Also, it makes sense for some types to return, e.g., a boost::tribool; Boost.Interval is such an example. Though convertible to bool, a user may be interested that the result of operator< is, specfically, a boost::tribool, and, e.g., throw if the result is indeterminate.
Thank you for the information regarding possible uses of these type traits. I had the idea in my head that there was no possible use other than added convenience for library writers. Your examples clearly contradict that. Based on the false idea I held I reasoned that there were only potential abuses, and no potential benefits.
It seems like if all of the operators are supported it will encourage boost developers to move complexity from the back end (what boost developers see) to the front end (what users see).
If a boost developer requires a user defined type to specify more than "<" for the purpose of doing any of "<, <=, >, >=, ==, and !=", it effectively moves complexity to the front end.
-Seth
Are you saying that boost's current quality controls are insufficient to catch unnecessary developer-imposed requirements on the client? ;)
Not at all! As a long time boost user I've always been very impressed with the quality of the libraries I've used. :-) -Seth

What immediately comes to my mind are expression templates (e.g., Boost.Proto). This is also a case where the return type is not necessarily convertible to bool. I don't think this library should decide whether it's appropriate for a client of this library to test operator< for such types; leave that decision for the client.
I did not think about expression templates but do you see any reason for asking if the expression template is comparable? Normally the user do not bother about expression templates (which is an implementation detail) but only about the result type which is a "normal" type? I may be wrong as I only know expression templates in the frame of uBlas. Frédéric

Frédéric Bron wrote:
What immediately comes to my mind are expression templates (e.g., Boost.Proto). This is also a case where the return type is not necessarily convertible to bool. I don't think this library should decide whether it's appropriate for a client of this library to test operator< for such types; leave that decision for the client.
I did not think about expression templates but do you see any reason for asking if the expression template is comparable? Normally the user do not bother about expression templates (which is an implementation detail) but only about the result type which is a "normal" type? I may be wrong as I only know expression templates in the frame of uBlas.
Frédéric
First, I'd like to say I think this would be a useful addition to Boost.TypeTraits, and I appreciate your work Frederic ;) There were 2 points that I raised in response to a previous message that I'd like to clearly distinguish: 1) You cannot generally express all comparison operators in terms of operator<, or even in terms of a combination of operator< and operator==. Expression templates were one example I thought of (e.g., Boost.Proto), but it just seems wrong to me for a library to dictate the semantic relationship among the comparison operators. operator< and operator== are enough *most* of the time to get the other comparison operators, but I don't think *all* of the time (though a good example escapes me at the moment). I gather you don't take issue with this, as you provide metafunctions corresponding to all the comparison operators. 2) At the very least, boost::tribool is a legitimate return value (in addition to bool) for any of the comparison operators, which indicates to me that the interface should allow specification of the result type of a comparison operator. - Jeff

First, I'd like to say I think this would be a useful addition to Boost.TypeTraits, and I appreciate your work Frederic ;) Thanks!
2) At the very least, boost::tribool is a legitimate return value (in addition to bool) for any of the comparison operators, which indicates to me that the interface should allow specification of the result type of a comparison operator.
I will try to make the checked return type a template defaulted to bool but I am not sure it will work. Frédéric
participants (15)
-
Andrey Semashev
-
Christian Schladetsch
-
David Abrahams
-
Frédéric Bron
-
Gottlob Frege
-
Jeffrey Bosboom
-
Jeffrey Hellrung
-
joel
-
John Maddock
-
Patrick Horgan
-
Ronald Garcia
-
Seth Bunce
-
Steven Watanabe
-
Stewart, Robert
-
Zachary Turner