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

Hi folks, I would like to propose to your review the following addition to the type_traits library. The addition is attached to this email and also available in the sandbox at https://svn.boost.org/trac/boost/browser/sandbox/type_traits The purpose of the addition is to add type traits to detect if type T is comparable in the sens of <, <=, >, >=, == or != operators, i.e. if t1<t2 has a sens when t1 and t2 are of type T (same for <=, >, >=, ==, !=). The following traits are added: is_equal_to_comparable<T> is_greater_comparable<T> is_greater_equal_comparable<T> is_less_comparable<T> is_less_equal_comparable<T> is_not_equal_to_comparable<T> The names are based on the corresponding names of the standard template library (<functional> header, section 20.3.3 of the standard). 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. I must add that I did not invent the code (I obtained some help from the boost mailing list) but I put it together in a form that should be acceptable to boost. Regards, Frédéric

Anybody wanting to review this very short addition to type_traits? I can add that one important question of the review is: should we report that T is less comparable if T<T returns void? Currently it returns true but the implementation generates warnings in g++. However, I imagine that if someone wants to know if T<T works, it is to get the result (this is my use of it). Regards, Frédéric De : Frédéric Bron <frederic.bron@m4x.org> Date : 10 août 2009 21:29 Objet : review request: addition to type_traits library of is_less_comparable<T> and others À : boost@lists.boost.org Hi folks, I would like to propose to your review the following addition to the type_traits library. The addition is attached to this email and also available in the sandbox at https://svn.boost.org/trac/boost/browser/sandbox/type_traits The purpose of the addition is to add type traits to detect if type T is comparable in the sens of <, <=, >, >=, == or != operators, i.e. if t1<t2 has a sens when t1 and t2 are of type T (same for <=, >, >=, ==, !=). The following traits are added: is_equal_to_comparable<T> is_greater_comparable<T> is_greater_equal_comparable<T> is_less_comparable<T> is_less_equal_comparable<T> is_not_equal_to_comparable<T> The names are based on the corresponding names of the standard template library (<functional> header, section 20.3.3 of the standard). 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. I must add that I did not invent the code (I obtained some help from the boost mailing list) but I put it together in a form that should be acceptable to boost. Regards, Frédéric

AMDG Frédéric Bron wrote:
Anybody wanting to review this very short addition to type_traits?
I can add that one important question of the review is: should we report that T is less comparable if T<T returns void? Currently it returns true but the implementation generates warnings in g++. However, I imagine that if someone wants to know if T<T works, it is to get the result (this is my use of it).
To be strictly correct, it should only return true if the result is convertible to bool. In Christ, Steven Watanabe

[subject adjusted a bit] Frédéric Bron wrote:
Anybody wanting to review this very short addition to type_traits?
remove_cv.html is not in the archive (the link from index.html was broken). The links for the additions are not in alphabetical order on index.html. Even if you didn't invent the code, you assembled and documented it, so your name should be in the copyright notice (in the documentation), right? Add others to the list as you think appropriate. Your HTML formatting differs from the original, though that could be the means by which it was generated rather than a difference in the formatting of your files. Your content will appear in a single page, reference.html, if added to Boost.TypeTraits, so why create separate pages now? The description of is_greater_comparable should be more specific: If a value of type T can be compared with another using operator > to determine whether the first is greater than the other, then inherits from true_type, otherwise inherits from false_type. That rewrite anticipates the question you asked, below. I think is_greater_than can be less restrictive than it is. std::greater() provides an operator that permits comparing unrelated types as T's. Therefore, I wonder if this would work: template <class T, class U = T> struct is_greater_comparable; Then, is_greater_comparable<int, float> would inherit from true_type. Similar changes would apply to the other comparison types.
I can add that one important question of the review is: should we report that T is less comparable if T<T returns void?
I think that is not appropriate as the whole point of less-than-comparable is to be able to determine whether one instance is less than another, not that "operator <" is supported. The same applies to the other comparison operators. _____ 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.

I think is_greater_than can be less restrictive than it is. std::greater() provides an operator that permits comparing unrelated types as T's. Therefore, I wonder if this would work:
template <class T, class U = T> struct is_greater_comparable;
Then, is_greater_comparable<int, float> would inherit from true_type.
I have tried to do that but cannot understand why it does not work. Can you try this example and tell me why it does not give false each time? Frédéric #include <iostream> #include <boost/type_traits/remove_cv.hpp> #include <boost/type_traits/integral_constant.hpp> namespace boost { namespace detail { // This namespace ensures that ADL doesn't mess things up. namespace is_less_comparable_impl { // a type returned from comparison operator when no such operator is found in the // type's own namespace struct tag { } ; // any soaks up implicit conversions and makes the following // comparison operators less-preferred than any other such operators that // might be found via ADL. struct any { template <class T> any(T const&) ; } ; tag operator<(const any&, const any&) ; // In case a comparison operator is found that returns void, we'll use (x comp x),0 tag operator,(tag, int) ; // two check overloads help us identify which comparison operator was picked // check(tag) returns a reference to char[2] (sizeof>1) char (& check(tag))[2] ; // check(not tag) returns char (sizeof==1) char check(int) ; template < typename T, typename U > struct is_less_comparable_impl { static typename boost::remove_cv<T>::type &t ; static typename boost::remove_cv<T>::type &u ; static const bool value = sizeof(check(((t<u), 0))) == 1 ; } ; } // namespace impl } // namespace detail template< typename T, typename U=T > struct is_less_comparable : boost::integral_constant<bool,(::boost::detail::is_less_comparable_impl::is_less_comparable_impl<T,U>::value)> { } ; } // namespace boost class NotComparable { } ; int main() { std::cout << std::boolalpha ; std::cout << boost::is_less_comparable<double, NotComparable>::value << "\t(double<NotComparable)\n" ; std::cout << boost::is_less_comparable<NotComparable, double>::value << "\t(NotComparable>double)\n" ; return 0 ; }

Frédéric Bron wrote:
I think is_greater_than can be less restrictive than it is. std::greater() provides an operator that permits comparing unrelated types as T's. Therefore, I wonder if this would work:
template <class T, class U = T> struct is_greater_comparable;
Then, is_greater_comparable<int, float> would inherit from true_type.
I have tried to do that but cannot understand why it does not work. Can you try this example and tell me why it does not give false each time?
Yes [snip]
template < typename T, typename U > struct is_less_comparable_impl { static typename boost::remove_cv<T>::type &t ; static typename boost::remove_cv<T>::type &u ;
Change that last line to: static typename boost::remove_cv<U>::type &u ; _____ 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.

remove_cv.html is not in the archive (the link from index.html was broken).
It is already part of boost but as I use it I should include it in the tarball. Sorry.
The links for the additions are not in alphabetical order on index.html.
Yes, I thought it was better here to have is_less close to is_less_equal but I have no strong opinion on that and can come back to alphabetical order.
Even if you didn't invent the code, you assembled and documented it, so your name should be in the copyright notice (in the documentation), right? Add others to the list as you think appropriate.
OK, I added Roman Perepelitsa and my-self. Roman, if you are still reading this mailing list, do you agree to have your name in the copyright?
Your HTML formatting differs from the original, though that could be the means by which it was generated rather than a difference in the formatting of your files.
I think so because I applied the method recommended by Beman.
Your content will appear in a single page, reference.html, if added to Boost.TypeTraits, so why create separate pages now?
I do not understand your point here. Do you mean I could have all traits on the same page and only one line in reference.html?
The description of is_greater_comparable should be more specific:
If a value of type T can be compared with another using operator > to determine whether the first is greater than the other, then inherits from true_type, otherwise inherits from false_type.
OK for that.
I think is_greater_than can be less restrictive than it is. std::greater() provides an operator that permits comparing unrelated types as T's. Therefore, I wonder if this would work:
template <class T, class U = T> struct is_greater_comparable;
Then, is_greater_comparable<int, float> would inherit from true_type.
Thank you for suggestion (applied) and help finding my error (shame on me).
I can add that one important question of the review is: should we report that T is less comparable if T<T returns void?
I think that is not appropriate as the whole point of less-than-comparable is to be able to determine whether one instance is less than another, not that "operator <" is supported.
Here, I cannot find a way to get the return type of operator<(T, U). The only thing I can do is: 1. do not treat "void operator<(T, U)" => thus using is_less_comparable<T, U> if operator<(T, U) returns void will be a compilation error => no compiler warning for the ternary operator, trick 2. treat "void operator<(T, U)" => must use the ternary operator, trick => compiler warning => is_less_comparable<T, U> is true_type if operator<(T, U) exists and returns void (no compiler error) I am in favour of solution 1 because when we need is_less_comparable<T, U> it is obviously to later do something like "bool result=t<u". Then with solution 1 the compilation error would come when is_less_comparable<T, U> is invoked and with solution 2 it would come when t<u is invoked. So in either case, there will be a compilation error somewhere. Other issue: I do not see how I can test the return type of operator< to check if it is convertible to bool. Frédéric

AMDG Frédéric Bron wrote:
Here, I cannot find a way to get the return type of operator<(T, U). The only thing I can do is:
1. do not treat "void operator<(T, U)" => thus using is_less_comparable<T, U> if operator<(T, U) returns void will be a compilation error => no compiler warning for the ternary operator, trick 2. treat "void operator<(T, U)" => must use the ternary operator, trick => compiler warning => is_less_comparable<T, U> is true_type if operator<(T, U) exists and returns void (no compiler error)
I am in favour of solution 1 because when we need is_less_comparable<T, U> it is obviously to later do something like "bool result=t<u". Then with solution 1 the compilation error would come when is_less_comparable<T, U> is invoked and with solution 2 it would come when t<u is invoked. So in either case, there will be a compilation error somewhere.
Other issue: I do not see how I can test the return type of operator< to check if it is convertible to bool.
Once you know that operator< exists and returns non-void, you can use simple overload resolution. typedef char no; struct yes { no dummy[2]; }; yes is_bool(bool); no is_bool(...); sizeof(is_bool(T() < U())) == sizeof(yes); In Christ, Steven Watanabe

Steven Watanabe wrote:
Once you know that operator< exists and returns non-void, you can use simple overload resolution.
typedef char no; struct yes { no dummy[2]; };
yes is_bool(bool); no is_bool(...);
sizeof(is_bool(T() < U())) == sizeof(yes);
That assumes T and U have a default constructor. Use a factory function to create T's and U's instead. _____ 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.

Once you know that operator< exists and returns non-void, you can use simple overload resolution.
typedef char no; struct yes { no dummy[2]; };
yes is_bool(bool); no is_bool(...);
sizeof(is_bool(T() < U())) == sizeof(yes);
That assumes T and U have a default constructor. Use a factory function to create T's and U's instead.
Very nice but I still cannot handle "convertible to bool". I have tried to use boost::is_convertible but could not. Here is my code and you can see that StrangeComparable objects give the bad result. Also I have manually entered all built-in types convertible to bool so that they are handled properly but new types convertible to bool are not handled. Frédéric #include <iostream> #include <boost/type_traits/remove_cv.hpp> #include <boost/type_traits/integral_constant.hpp> namespace boost { namespace detail { // This namespace ensures that ADL doesn't mess things up. namespace is_less_comparable_impl { // a type returned from comparison operator when no such operator is found in the // type's own namespace struct tag { } ; // any soaks up implicit conversions and makes the following // comparison operators less-preferred than any other such operators that // might be found via ADL. struct any { template <class T> any(T const&) ; } ; tag operator<(const any&, const any&) ; // two check overloads help us identify which comparison operator was picked // check(not convertible to bool) returns a reference to char[2] (sizeof>1) template <typename T> char (& check(T))[2] ; // check(convertible to bool) returns char (sizeof==1) template <typename T> char check(T*) ; char check(bool) ; char check(char) ; char check(signed char) ; char check(unsigned char) ; char check(short) ; char check(unsigned short) ; char check(int) ; char check(unsigned int) ; char check(long) ; char check(unsigned long) ; template < typename T, typename U > struct is_less_comparable_impl { static typename boost::remove_cv<T>::type &t ; static typename boost::remove_cv<U>::type &u ; static const bool value = sizeof(check(t<u))==1 ; } ; } // namespace impl } // namespace detail template< typename T, typename U=T > struct is_less_comparable : boost::integral_constant<bool,(::boost::detail::is_less_comparable_impl::is_less_comparable_impl<T,U>::value)> { } ; } // namespace boost // for testing struct NotComparable { } ; struct Comparable { bool operator<(const Comparable&) const ; } ; struct IntComparable { int operator<(const IntComparable&) ; } ; struct PtrComparable { void *operator<(const PtrComparable&) ; } ; // this one has a strange operator<: it returns a non standard type convertible to bool struct Convertible { operator bool () const ; } ; struct StrangeComparable { Convertible operator<(const StrangeComparable&) ; } ; #define TEST1(T)\ std::cout << boost::is_less_comparable<T, T>::value << "\t(" << #T << '<' << #T << ")\n" #define TEST2(T1,T2)\ std::cout << boost::is_less_comparable<T1, T2>::value << "\t(" << #T1 << '<' << #T2 << ")\n" ;\ std::cout << boost::is_less_comparable<T2, T1>::value << "\t(" << #T2 << '<' << #T1 << ")\n" int main() { std::cout << std::boolalpha ; TEST1(NotComparable) ; TEST1(Comparable) ; TEST1(IntComparable) ; TEST1(PtrComparable) ; TEST1(StrangeComparable) ; TEST2(double, NotComparable) ; TEST2(int, NotComparable) ; TEST2(double, IntComparable) ; TEST2(int, IntComparable) ; TEST2(double, Comparable) ; TEST2(int, Comparable) ; TEST2(int, char) ; TEST2(int, short) ; TEST2(int, long) ; TEST2(int, float) ; TEST2(int, double) ; return 0 ; }

Hi, ----- Original Message ----- From: "Frédéric Bron" <frederic.bron@m4x.org> To: <boost@lists.boost.org> Sent: Tuesday, September 01, 2009 9:38 PM Subject: Re: [boost] [type_traits] adding is_less_comparable<T>, etc.
// two check overloads help us identify which comparison operator was picked
// check(not convertible to bool) returns a reference to char[2] (sizeof>1) template <typename T> char (& check(T))[2] ;
// check(convertible to bool) returns char (sizeof==1) template <typename T> char check(T*) ; char check(bool) ; char check(char) ; char check(signed char) ; char check(unsigned char) ; char check(short) ; char check(unsigned short) ; char check(int) ; char check(unsigned int) ; char check(long) ; char check(unsigned long) ;
template < typename T, typename U > struct is_less_comparable_impl { static typename boost::remove_cv<T>::type &t ; static typename boost::remove_cv<U>::type &u ; static const bool value = sizeof(check(t<u))==1 ;
As sugested by Steven and Robert, have you tried typedef char no; struct yes { no dummy[2]; }; yes is_bool(bool); no is_bool(...); template < typename T, typename U > struct is_less_comparable_impl { static typename boost::remove_cv<T>::type &t ; static typename boost::remove_cv<U>::type &u ; static const bool value = sizeof(is_bool(t<u))==sizeof(yes) ; } instead of
// two check overloads help us identify which comparison operator was picked
// check(not convertible to bool) returns a reference to char[2] (sizeof>1) template <typename T> char (& check(T))[2] ;
// check(convertible to bool) returns char (sizeof==1) template <typename T> char check(T*) ; char check(bool) ; char check(char) ; char check(signed char) ; char check(unsigned char) ; char check(short) ; char check(unsigned short) ; char check(int) ; char check(unsigned int) ; char check(long) ; char check(unsigned long) ;
template < typename T, typename U > struct is_less_comparable_impl { static typename boost::remove_cv<T>::type &t ; static typename boost::remove_cv<U>::type &u ; static const bool value = sizeof(check(t<u))==1 ;
Best, Vicente

vicente.botet wrote:
As sugested by Steven and Robert, have you tried
typedef char no; struct yes { no dummy[2]; };
yes is_bool(bool); no is_bool(...); template < typename T, typename U > struct is_less_comparable_impl { static typename boost::remove_cv<T>::type &t ; static typename boost::remove_cv<U>::type &u ; static const bool value = sizeof(is_bool(t<u))==sizeof(yes) ; }
Right. The following is what he needs, I think. This compiles, but I didn't dig into the results to verify it. typedef char fail; struct pass { fail dummy[2]; }; pass is_bool(bool); fail is_bool(...); template <class T> T make_a(); template < typename T, typename U > struct is_less_comparable_impl { typedef typename boost::remove_cv<T>::type t; typedef typename boost::remove_cv<U>::type u; static const bool value = sizeof(is_bool(make_a<t>() < make_a<u>())) == sizeof(pass); }; _____ 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.

Thanks for all contributions. It works very fine now. 1. the key is the use of the ... argument instead of a template parameter 2. there is no difference between using a factory function and using a static reference to a value of the type -> I prefer the static reference 3. there is no difference between using pass/fail and return char or char[2] but pass/fail is easier to read -> I keep it So the following 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 If you agree with the implementation, I will modify the sandbox accordingly and propose a new submission. Can I add your names in the copyright notice ? (Robert Stewart, Steven Watanabe, Roman Perepelitsa). Frédéric #include <iostream> #include <boost/type_traits/remove_cv.hpp> #include <boost/type_traits/integral_constant.hpp> namespace boost { namespace detail { // This namespace ensures that ADL does not mess things up. namespace is_less_comparable_impl { // a type returned from comparison operator when no such operator is found in the // type's own namespace struct tag { } ; // any soaks up implicit conversions and makes the following // comparison operators less-preferred than any other such operators that // might be found via ADL. struct any { template <class T> any(T const&) ; } ; // when operator< is not available, this one is used tag operator<(const any&, const any&) ; typedef char pass ; struct fail { char dummy[2] ; } ; pass is_bool(bool) ; // this version is preferred for types convertible to bool fail is_bool(...) ; // this version is used otherwise template < typename T, typename U > struct is_less_comparable_impl { static typename boost::remove_cv<T>::type &t ; static typename boost::remove_cv<U>::type &u ; static const bool value = sizeof(is_bool(t<u)) == sizeof(pass) ; } ; } // namespace impl } // namespace detail template< typename T, typename U=T > struct is_less_comparable : boost::integral_constant<bool,(::boost::detail::is_less_comparable_impl::is_less_comparable_impl<T,U>::value)> { } ; } // namespace boost // for testing struct NotComparable { } ; struct Comparable { bool operator<(const Comparable&) const ; } ; struct IntComparable { int operator<(const IntComparable&) ; } ; struct PtrComparable { void *operator<(const PtrComparable&) ; } ; // this one has a strange operator<: it returns a non standard type convertible to bool struct Convertible { operator bool () const ; } ; struct StrangeComparable { Convertible operator<(const StrangeComparable&) ; } ; struct VoidComparable { void operator<(const VoidComparable&) ; } ; #define TEST1(T)\ std::cout << boost::is_less_comparable<T, T>::value << "\t(" << #T << '<' << #T << ")\n" #define TEST2(T1,T2)\ std::cout << boost::is_less_comparable<T1, T2>::value << "\t(" << #T1 << '<' << #T2 << ")\n" ;\ std::cout << boost::is_less_comparable<T2, T1>::value << "\t(" << #T2 << '<' << #T1 << ")\n" int main() { std::cout << std::boolalpha ; TEST1(NotComparable) ; TEST1(Comparable) ; TEST1(IntComparable) ; TEST1(PtrComparable) ; TEST1(StrangeComparable) ; // the following line triggers a compile time error //TEST1(VoidComparable) ; TEST2(double, NotComparable) ; TEST2(int, NotComparable) ; TEST2(double, IntComparable) ; TEST2(int, IntComparable) ; TEST2(double, Comparable) ; TEST2(int, Comparable) ; TEST2(int, char) ; TEST2(int, short) ; TEST2(int, long) ; TEST2(int, float) ; TEST2(int, double) ; return 0 ; }

Thanks for all contributions. It works very fine now. 1. the key is the use of the ... argument instead of a template parameter 2. there is no difference between using a factory function and using a static reference to a value of the type -> I prefer the static reference 3. there is no difference between using pass/fail and return char or char[2] but pass/fail is easier to read -> I keep it
With apologies for not keeping up with discussion: there is also boost/type_traits/detail/yes_no_type.hpp that defines a yes_type and a no_type along these lines. John.

Hi guys: I found that boost thread was build as lib(.so or .a) with default setting Possible to use it without lib? cause I cann't find any *.cpp files in thread directory. Thanks jon

I found that boost thread was build as lib(.so or .a) with default setting
Possible to use it without lib? cause I cann't find any *.cpp files in thread directory.
https://svn.boost.org/trac/boost/browser/trunk/libs/thread/src/win32 Probably possible but it likely will be some kind of pain, you're better off with using the .lib/.so. Philippe

AFAIK thread requires the source to be compiled: https://svn.boost.org/svn/boost/trunk/libs/thread/src/ https://svn.boost.org/svn/boost/trunk/libs/thread/src/pthread https://svn.boost.org/svn/boost/trunk/libs/thread/src/win32 -Matt jon_zhou@agilent.com wrote:
Hi guys:
I found that boost thread was build as lib(.so or .a) with default setting Possible to use it without lib? cause I cann't find any *.cpp files in thread directory.
Thanks jon

Thanks for all contributions. It works very fine now. 1. the key is the use of the ... argument instead of a template parameter 2. there is no difference between using a factory function and using a static reference to a value of the type -> I prefer the static reference 3. there is no difference between using pass/fail and return char or char[2] but pass/fail is easier to read -> I keep it
So the following 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
If you agree with the implementation, I will modify the sandbox accordingly and propose a new submission. Can I add your names in the copyright notice ? (Robert Stewart, Steven Watanabe, Roman Perepelitsa).
Please, are you happy with that? I would be very happy if included in the next version and deadline is 13th of Septembre. Thanks in advance, Frédéric
#include <iostream> #include <boost/type_traits/remove_cv.hpp> #include <boost/type_traits/integral_constant.hpp>
namespace boost { namespace detail {
// This namespace ensures that ADL does not mess things up. namespace is_less_comparable_impl {
// a type returned from comparison operator when no such operator is found in the // type's own namespace struct tag { } ;
// any soaks up implicit conversions and makes the following // comparison operators less-preferred than any other such operators that // might be found via ADL. struct any { template <class T> any(T const&) ; } ;
// when operator< is not available, this one is used tag operator<(const any&, const any&) ;
typedef char pass ; struct fail { char dummy[2] ; } ;
pass is_bool(bool) ; // this version is preferred for types convertible to bool fail is_bool(...) ; // this version is used otherwise
template < typename T, typename U > struct is_less_comparable_impl { static typename boost::remove_cv<T>::type &t ; static typename boost::remove_cv<U>::type &u ; static const bool value = sizeof(is_bool(t<u)) == sizeof(pass) ; } ;
} // namespace impl } // namespace detail
template< typename T, typename U=T > struct is_less_comparable : boost::integral_constant<bool,(::boost::detail::is_less_comparable_impl::is_less_comparable_impl<T,U>::value)> { } ;
} // namespace boost
// for testing struct NotComparable { } ; struct Comparable { bool operator<(const Comparable&) const ; } ; struct IntComparable { int operator<(const IntComparable&) ; } ; struct PtrComparable { void *operator<(const PtrComparable&) ; } ; // this one has a strange operator<: it returns a non standard type convertible to bool struct Convertible { operator bool () const ; } ; struct StrangeComparable { Convertible operator<(const StrangeComparable&) ; } ; struct VoidComparable { void operator<(const VoidComparable&) ; } ;
#define TEST1(T)\ std::cout << boost::is_less_comparable<T, T>::value << "\t(" << #T << '<' << #T << ")\n"
#define TEST2(T1,T2)\ std::cout << boost::is_less_comparable<T1, T2>::value << "\t(" << #T1 << '<' << #T2 << ")\n" ;\ std::cout << boost::is_less_comparable<T2, T1>::value << "\t(" << #T2 << '<' << #T1 << ")\n"
int main() { std::cout << std::boolalpha ;
TEST1(NotComparable) ; TEST1(Comparable) ; TEST1(IntComparable) ; TEST1(PtrComparable) ; TEST1(StrangeComparable) ; // the following line triggers a compile time error //TEST1(VoidComparable) ;
TEST2(double, NotComparable) ; TEST2(int, NotComparable) ; TEST2(double, IntComparable) ; TEST2(int, IntComparable) ; TEST2(double, Comparable) ; TEST2(int, Comparable) ;
TEST2(int, char) ; TEST2(int, short) ; TEST2(int, long) ; TEST2(int, float) ; TEST2(int, double) ; return 0 ; }

Frédéric Bron wrote:
Thanks for all contributions. It works very fine now.
[snip]
Please, are you happy with that? I would be very happy if included in the next version and deadline is 13th of Septembre.
You've allowed just over a day between your two posts. Don't be so impatient. I was out yesterday due to possibly breaking my shoulder. Not everyone reads and responds daily. You can always improve things later, if need be. _____ 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.

Frédéric Bron wrote:
2. there is no difference between using a factory function and using a static reference to a value of the type -> I prefer the static reference
This is compile time code, so there should be no runtime observable result, but I wonder if its possible that the references would trigger some compiler to produce symbols the factory functions would not. The references are definitely superior to instances, but I think the factory function approach is the accepted practice for this purpose.
3. there is no difference between using pass/fail and return char or char[2] but pass/fail is easier to read -> I keep it
As John Maddock pointed out, there are other types to use for such purposes in Type Traits and other libraries. It would be appropriate to use the Type Traits types here.
So the following 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
I'd like to see something done to detect the void return, with a meaningful error message though. It is possible to extract the return type from a function pointer and check for that being the same as void, so it might be possible to form a pointer to the operator. You'd have to know whether the argument types are built-in types, in which case you can short-circuit the answer. If not, you'd have to determine whether the operator is a free function or a member in order to create the proper pointer type. Once you have that, you can extract the return type and compare it. Finally, you can use BOOST_STATIC_ASSERT to complain if the return type is void.
Can I add your names in the copyright notice ? (Robert Stewart, Steven Watanabe, Roman Perepelitsa).
Yes, thank you. _____ 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.

Please find at https://svn.boost.org/trac/boost/browser/sandbox/type_traits and at http://dl.free.fr/tK0BwIzYy my new proposal according to review comments. * now using 2 template parameters so that it is possible to check if 2 different types are comparable * now using factory functions instead of static references * now using type_traits yes_type and no_type * the doc. is alphabetically sorted * the doc. has been updated for clarity * the tests have been modified to include new possibilities 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; on this point, please have a look at the comment below. Thank you for your review of this new version (please find below the main piece of code). Frédéric
I'd like to see something done to detect the void return, with a meaningful error message though. It is possible to extract the return type from a function pointer and check for that being the same as void, so it might be possible to form a pointer to the operator. You'd have to know whether the argument types are built-in types, in which case you can short-circuit the answer. If not, you'd have to determine whether the operator is a free function or a member in order to create the proper pointer type. Once you have that, you can extract the return type and compare it. Finally, you can use BOOST_STATIC_ASSERT to complain if the return type is void.
This looks too difficult for me! Can we keep it for a future update, unless you can show me how to do it? type_traits/detail/is_comparable.hpp #include <boost/config.hpp> #include <boost/type_traits/remove_cv.hpp> #include <boost/type_traits/integral_constant.hpp> #include <boost/type_traits/detail/yes_no_type.hpp> // should be the last #include #include <boost/type_traits/detail/bool_trait_def.hpp> namespace boost { namespace detail { // This namespace ensures that ADL does not mess things up. namespace BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl) { // a type returned from comparison operator when no such operator is found in the // type's own namespace struct tag { } ; // any soaks up implicit conversions and makes the following // comparison operators less-preferred than any other such operators that // might be found via ADL. struct any { template <class T> any(T const&) ; } ; // when operator< is not available, this one is used tag operator BOOST_TT_TRAIT_OP (const any&, const any&) ; ::boost::type_traits::yes_type is_bool(bool) ; // this version is preferred for types convertible to bool ::boost::type_traits::no_type is_bool(...) ; // this version is used otherwise template < typename T, typename U > struct BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl) { static T make_T() ; static U make_U() ; static const bool value = sizeof(is_bool(make_T() BOOST_TT_TRAIT_OP make_U())) == sizeof(::boost::type_traits::yes_type) ; } ; } // namespace impl } // namespace detail BOOST_TT_AUX_BOOL_TRAIT_DEF2(BOOST_TT_TRAIT_NAME,T,U=T,(::boost::detail::BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl)::BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl)<T,U>::value)) } // namespace boost #include <boost/type_traits/detail/bool_trait_undef.hpp>

AMDG Frédéric Bron wrote:
Please find at https://svn.boost.org/trac/boost/browser/sandbox/type_traits and at http://dl.free.fr/tK0BwIzYy my new proposal according to review comments.
* now using 2 template parameters so that it is possible to check if 2 different types are comparable * now using factory functions instead of static references * now using type_traits yes_type and no_type * the doc. is alphabetically sorted * the doc. has been updated for clarity * the tests have been modified to include new possibilities
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; on this point, please have a look at the comment below.
There are some tricks involving overloading the comma operator (The built-in comma operator can take a void argument.) Unfortunately, I've never been able to come up with a completely bulletproof method that can handle types which themselves overload the comma operator.
I'd like to see something done to detect the void return, with a meaningful error message though. It is possible to extract the return type from a function pointer and check for that being the same as void, so it might be possible to form a pointer to the operator. You'd have to know whether the argument types are built-in types, in which case you can short-circuit the answer. If not, you'd have to determine whether the operator is a free function or a member in order to create the proper pointer type. Once you have that, you can extract the return type and compare it. Finally, you can use BOOST_STATIC_ASSERT to complain if the return type is void.
This looks too difficult for me! Can we keep it for a future update, unless you can show me how to do it?
There is no way to get a pointer to the correct operator<. operator< may be overloaded, in which case you need to know the signature (at least approximately) to get a pointer. Even worse, since ADL doesn't apply when taking the address of a function, there is no way to guarantee that the correct function is in the overload set at all. In Christ, Steven Watanabe

Please find at https://svn.boost.org/trac/boost/browser/sandbox/type_traits and at http://dl.free.fr/tK0BwIzYy my new proposal according to review comments.
* now using 2 template parameters so that it is possible to check if 2 different types are comparable * now using factory functions instead of static references * now using type_traits yes_type and no_type * the doc. is alphabetically sorted * the doc. has been updated for clarity * the tests have been modified to include new possibilities
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; on this point, please have a look at the comment below.
Thank you for your review of this new version (please find below the main piece of code).
Does anybody have other comments on this small addition? Regards, Frédéric

Hi, is it possible to conclude on that? Do we need more reviewers? Is the issue with operator< returning void a stopper for including the actual code in the library? Regards, Frédéric
Please find at https://svn.boost.org/trac/boost/browser/sandbox/type_traits and at http://dl.free.fr/tK0BwIzYy my new proposal according to review comments.
* now using 2 template parameters so that it is possible to check if 2 different types are comparable * now using factory functions instead of static references * now using type_traits yes_type and no_type * the doc. is alphabetically sorted * the doc. has been updated for clarity * the tests have been modified to include new possibilities
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; on this point, please have a look at the comment below.
Thank you for your review of this new version (please find below the main piece of code).
Frédéric
I'd like to see something done to detect the void return, with a meaningful error message though. It is possible to extract the return type from a function pointer and check for that being the same as void, so it might be possible to form a pointer to the operator. You'd have to know whether the argument types are built-in types, in which case you can short-circuit the answer. If not, you'd have to determine whether the operator is a free function or a member in order to create the proper pointer type. Once you have that, you can extract the return type and compare it. Finally, you can use BOOST_STATIC_ASSERT to complain if the return type is void.
This looks too difficult for me! Can we keep it for a future update, unless you can show me how to do it?
type_traits/detail/is_comparable.hpp
#include <boost/config.hpp> #include <boost/type_traits/remove_cv.hpp> #include <boost/type_traits/integral_constant.hpp> #include <boost/type_traits/detail/yes_no_type.hpp>
// should be the last #include #include <boost/type_traits/detail/bool_trait_def.hpp>
namespace boost { namespace detail {
// This namespace ensures that ADL does not mess things up. namespace BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl) {
// a type returned from comparison operator when no such operator is found in the // type's own namespace struct tag { } ;
// any soaks up implicit conversions and makes the following // comparison operators less-preferred than any other such operators that // might be found via ADL. struct any { template <class T> any(T const&) ; } ;
// when operator< is not available, this one is used tag operator BOOST_TT_TRAIT_OP (const any&, const any&) ;
::boost::type_traits::yes_type is_bool(bool) ; // this version is preferred for types convertible to bool ::boost::type_traits::no_type is_bool(...) ; // this version is used otherwise
template < typename T, typename U > struct BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl) { static T make_T() ; static U make_U() ; static const bool value = sizeof(is_bool(make_T() BOOST_TT_TRAIT_OP make_U())) == sizeof(::boost::type_traits::yes_type) ; } ;
} // namespace impl } // namespace detail
BOOST_TT_AUX_BOOL_TRAIT_DEF2(BOOST_TT_TRAIT_NAME,T,U=T,(::boost::detail::BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl)::BOOST_JOIN(BOOST_TT_TRAIT_NAME,_impl)<T,U>::value))
} // namespace boost
#include <boost/type_traits/detail/bool_trait_undef.hpp>
-- Frédéric Bron ----------------------------------------------------------- Frédéric Bron (frederic.bron@m4x.org) Villa des Quatre Chemins, Centre Hospitalier, BP 208 38506 VOIRON CEDEX tél. : (33) 4 76 67 17 27
participants (8)
-
Frédéric Bron
-
John Maddock
-
jon_zhou@agilent.com
-
Matt Chambers
-
Philippe Vaucher
-
Steven Watanabe
-
Stewart, Robert
-
vicente.botet