review request: addition to type_traits library of has_operator_xxx

I would like to propose to your review the following addition to the type_traits library, available at the following addresses: https://svn.boost.org/trac/boost/browser/sandbox/type_traits http://dl.free.fr/lRm4VL6WP/type_traits.tar.bz2 The purpose of the addition is to add type traits to detect if unary and binary operators can be applied to given types. For example, is "x<y" or "!x" or "x+y" meaningful? If required, the return type of such an expression is checked to know if it is convertible to a given type. Default behaviour is to not check the return type. The following traits are added: // binary operators: template < typename LHS, typename RHS=LHS, typename RET=void > == has_operator_equal_to != has_operator_not_equal_to > has_operator_greater >= has_operator_greater_equal < has_operator_less <= has_operator_less_equal + has_operator_plus - has_operator_minus * has_operator_multiplies / has_operator_divides % has_operator_modulus && has_operator_logical_and || has_operator_logical_or & has_operator_bit_and | has_operator_bit_or ^ has_operator_bit_xor // unary operators: template < typename RHS, typename RET=void > + has_operator_unary_plus - has_operator_unary_minus ! has_operator_logical_not This new version reflects the discussions we had on the list: http://thread.gmane.org/gmane.comp.lib.boost.devel/194625 In particular about the check or not of the return type. All operators are now included and not only comparison binary operators. Example: has_operator_less<LHS, RHS, RET>::value_type is the type bool. has_operator_less<int> inherits from true_type. has_operator_less<int, int, std::string> inherits from false_type. has_operator_unary_minus<int, long> inherits from true_type. has_operator_unary_minus<double, int> inherits from true_type. has_operator_unary_minus<int, std::string> inherits from false_type. Documentation is accessible at libs/type_traits/doc/html/index.html in the archive. Regards, Frédéric PS: tested with g++ 4.4.5 on i686-linux.

On 11/16/2010 03:49 PM, Frédéric Bron wrote:
I think several people who follow this list (myself included) have implementations of such traits lying around, so it makes sense to union the best parts of each one. I will take a look at your implementation and see if I have any suggestions. One thing that I've either found useful is to have an additional template parameter in addition to the parameter types and the return type, which is a Boost.MPL metapredicate intended to be applied to the actual result of the operation. The trait then evaluates to true only if the actual result type satisfies the given Boost.MPL metapredicate. This covers the cases where you may want to know more about the result type than whether it is convertible to some given type. For example, for the comparison operators, you may want to check if the result type of a particular comparison is *precisely* bool, rather than just convertible bool (that might be a rather contrived example, and if you don't believe this has a real use, I can (probably) come up with more real-world examples). It should be easy to support, it won't complicate existing use, and I believe it does provide some additional desirable flexibility. - Jeff

Jeffrey Lee Hellrung, Jr. wrote:
Perhaps checking for convertible-to-return-type could be the default predicate rather than adding another template parameter? _____ 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.

On 11/17/2010 6:58 AM, Stewart, Robert wrote:
How do you envision the interface then? Are you suggesting that the convertible-to-return-type type and the Boost.MPL metapredicate occupy the same template parameter? To be clear, I'm suggesting something like template< class T0, class T1 = T0, class Result = void, class ResultCond = boost::mpl::always< boost::true_type > > struct is_less_than_comparable; It seems to me that, by far, the common case is checking convertibility to some given type, so I think it makes sense to make the common case syntactically simple. If you want to avoid the extra template parameter, maybe you can wrap the predicate in something which could be detected in the Result template parameter, e.g., template< class P > struct operator_predicate; and then specialize is_less_than_comparable for that wrapper, so that the whole thing is template< class T0, class T1 = T0, class Result = void > struct is_less_than_comparable { ... }; template< class T0, class T1, class P > struct is_less_than_comparable< T0, T1, operator_predicate<P> > { ... }; Is this kind of what you had in mind? I personally prefer adding an extra template parameter. - Jeff

Jeffrey Lee Hellrung, Jr. wrote:
Yes
I'm thinking something like this: template < class T , class U = T , class Pred = boost::type_traits::result_converts_to<void>
struct is_less_than_comparable; boost::type_traits::result_converts_to<void> should inherit from boost::true_type, of course.
is_less_than_comparable<int, int, bool> would become is_less_than_comparable<int, int, result_converts_to<bool> > which is more verbose but also more obvious and indicative of the additional power.
That would be ideal, but I'm not sure there's a satisfying means to do so without imposing too much on the acceptable predicates. _____ 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.

On 11/17/2010 12:26 PM, Stewart, Robert wrote:
Okay. I don't like the verbosity but it's a minor issue.
The mechanism I gave doesn't impose anything on the predicate (as far as I can see). It does exclude return types that are instances of the wrapper, but I think that is acceptable. - Jeff

[This was stuck in my drafts folder unbeknownst to be.] Jeffrey Lee Hellrung, Jr. wrote:
It seems I scanned past your suggestion without understanding the purpose, but include it here for further discussion:
Your idea is to require users of is_less_than_comparable to wrap a predicate with operator_predicate, right? That leads to: is_less_than_comparable < int , int
is_less_than_comparable < int , int bool
is_less_than_comparable < int , int , operator_predicate<my_predicate_type>
That means the most common use case is easiest, the next most common use case is as convenient as it can be, and the least common use case is more verbose, but hardly onerous. I like it. The next question is whether the extra template parameter alternative is better: is_less_than_comparable < int , int , void , my_predicate_type
While anyone smart enough to create an MPL predicate for the purpose can deal with the extra template parameter, I find that really odd. I prefer your operator_predicate idea, despite the slightly greater verbosity. Here's another idea: template <class Pred, class> struct use_predicate { ... }; template <class From, class To> struct convertible { ... }; template < class T , class U , class V = void , template <class, class> P = convertible
struct is_less_than_comparable { ... }; If the convertibility check is done in convertible, then it needs two template parameters: the return type of the operator in question and V. For compatibility, use_predicate would also take two template parameters, but possibly wouldn't use the second. Thus, P<V, return-type-of-operator> is evaluated to yield is_less_than_comparable's result. (If the convertibility check is done in is_less_than_comparable, then convertible and use_predicate only need one template parameter.) Now the examples become: is_less_than_comparable < int , int
is_less_than_comparable < int , int bool
is_less_than_comparable < int , int , my_predicate_type , use_predicate
Obviously, convertible_to<W, void> always yields true/true_type. That looks cleaner (than the extra template parameter version, at least) and doable, though I haven't tried it. _____ 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.

----- Original Message ----- From: "Frédéric Bron" <frederic.bron@m4x.org> To: <boost@lists.boost.org> Sent: Wednesday, November 17, 2010 9:02 PM Subject: Re: [boost] review request: addition to type_traits library ofhas_operator_xxx
This would make type_traits library depend on the MPL library. Is it desirable? Frédéric _______________________________________________ I don't see the problem, as it depends already. Vicente

On 11/16/2010 6:49 PM, Frédéric Bron wrote:
Bravo ! I would very much like to see this happen also, as I can use this functionality in another library I would like to put in the sandbox. Without trying to make more work along your lines, would it be easy enough for you to add the left shift ( << ) and right shift ( >> ) binary operators, and/or the incrementable ( ++x and x++ ) and decrementable ( --x and x-- ) unary operators, or are any of these especially different or much more difficult cases ?

Nice work! As far as I can see, your implementation suffers the same problem as all other techniques for detection of operator / member function presence. It causes compilation error in case the operator in question is private. I believe this should be pointed out in the docs. And another question. Why are you using your own implementation of is_convertible instead of the one supplied by boost? -- Best regards, Alaxander Fokin, ru.elric@gmail.com.

It causes compilation error in case the operator in question is private. I believe this should be pointed out in the docs.
Good point, this will be added to the doc.
And another question. Why are you using your own implementation of is_convertible instead of the one supplied by boost?
I must try is_convertible. Not sure it handles correctly an operator that returns void. Frédéric

----- Original Message ----- From: "Edward Diener" <eldiener@tropicsoft.com> To: <boost@lists.boost.org> Sent: Wednesday, November 17, 2010 1:29 AM Subject: Re: [boost] review request: addition to type_traits library ofhas_operator_xxx On 11/16/2010 6:49 PM, Frédéric Bron wrote:
Bravo ! I would very much like to see this happen also, as I can use this functionality in another library I would like to put in the sandbox. Without trying to make more work along your lines, would it be easy enough for you to add the left shift ( << ) and right shift ( >> ) binary operators, and/or the incrementable ( ++x and x++ ) and decrementable ( --x and x-- ) unary operators, or are any of these especially different or much more difficult cases ? _______________________________________________ Hi, Thanks for pushing these long expected traits. I would expect all the C++ operators to be covered as ConceptTraits/OperatorTrats did. Next follows an extract from the documentation of this library for the missing operators. BTW, where are the new extensions documented, could you give a link? ::boost::has_address_of_op<T>::value Evaluates to true if the expression &a is valid. ::boost::has_dereference_op<T>::value Evaluates to true if the expression *a is valid. ::boost::has_member_access_op<T>::value Evaluates to true if the expression a-><member> is valid. The member access operator has to be a member function, so it can only be detected for classes and unions by specialising this trait. It gives default true for these. ::boost::has_subscript_op<T1 [,T2]>::value Evaluates to true if the expression a[b] is valid. The subscript operator has to be a member function, so it can only be detected for classes and unions by specialising this trait. It gives default true for these. ::boost::has_pointer_to_member_op<T1 [,T2]>::value Evaluates to true if the expression a->*b is valid. The pointer to member operator has to be a member function, so it can only be detected for classes and unions by specialising this trait. It gives default true for these. ::boost::has_comma_op<T1 [,T2]>::value Evaluates to true if the expression a,b is valid. ::boost::has_function_call_op<T,R [,P1, ...]>::value Evaluates to true if the expression R r = a(p1 [, ...]) is valid. The function call operator has to be a member function, so it can only be detected for classes and unions by specialising this trait. It gives default true for these. Best, Vicente

vicente.botet wrote:
Those names are not consistent with boost::has_new_operator. That is, you need to rename them like the following for consistency: s/has_operator_\(.+\)/has_\1_operator/ The result won't be perfect. For example, "has_operator_divides" should be renamed "has_division_operator." Renaming "has_new_operator" seems more appropriate, however. "has_operator_new" puts "operator" and "new" in the right order as the query is for operator new, not the new operator.
::boost::has_address_of_op<T>::value Evaluates to true if the expression &a is valid.
"has_operator_address_of" is more consistent with the above list, but "has_address_of_operator" is more consistent with boost::has_new_operator. The same discussion applies to the other traits.
::boost::has_member_access_op<T>::value
Evaluates to true if the expression a-><member> is valid.
That's called the member selection operator, so "has_member_selection_operator."
Why would the default be true? I'd expect the default to be false so only those classes for which a specialization is provided which may evaluate to true. (The only reason to specialize the trait, then, is for classes that define the operator.) The same question applies to the other traits that default to true. _____ 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.

The question of naming has already been discussed here : http://thread.gmane.org/gmane.comp.lib.boost.devel/194625 At that time, we preferred has_operator_xxx to has_xxx_operator. I agree that the whole type_traits library would need more consistency but this would break existing code. If this is not an issue, renaming should be rather straitforward providing we can all agree on new names. Frédéric

Frédéric Bron wrote:
There wasn't much discussion. You asked about "has_operator_less_than" versus "has_less_than_operator" and I suggested that the former is more consistent with C++ syntax, but that deviates from boost::has_new_operator.
At that time, we preferred has_operator_xxx to has_xxx_operator.
I still think its better.
I agree that the whole type_traits library would need more consistency but this would break existing code.
We could add has_operator_new as an alias for has_new_operator (or vice versa) and then the new ones would all be has_operator_* and would be consistent with has_operator_new. _____ 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.

On 11/17/2010 3:48 PM, Stewart, Robert wrote:
I agree also. Having all the operator traits begin with 'has_operator_' makes their use easier to remember. Alternatively I could even see putting all the operator traits in their own namespace of 'boost::operators', dropping the 'operator_' in their name, and allowing the end-user to surface them to just the 'boost' namespace via a using declaration as they like. But then again, I am a very strong supporter of namespaces and allowing the end-user to expose what they want from namespaces via 'using'.

On 17 November 2010 16:23, Edward Diener <eldiener@tropicsoft.com> wrote:
The problem, of course, is when you need them in a header, where using declarations cannot be scoped. This makes Boost.Multi-index a bit unwieldly, for example. I really don't mind if it gets spelled with namespaces instead of underscores, but a using declaration is not a motivation for it. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On 11/17/2010 8:59 PM, Nevin Liber wrote:
You are right about using directives but, I believe, it would always be possible to provide a header with using declarations for boost::operators which, when included, could bring them directly into the boost namespace if so desired. As a simple example: namespace XX { void AFunction(); } namespace YY { using XX::AFunction; } One can now refer to YY::AFunction() from some source code. I have 'hoisted' XX::AFunction() into another namespace so to speak.
I really don't mind if it gets spelled with namespaces instead of underscores, but a using declaration is not a motivation for it.
OK. But I prefer namespaces rather than decorating names with some common 'prefix' to show commonality. I personally believe as an end-user that there is the tendency to pile too much directly into the boost namespace.

Using namespaces, it would become: namespace boost { namespace operators { has_equal_to has_not_equal_to has_greater has_greater_equal has_less has_less_equal has_plus has_minus ... }} There would be a small inconsistency because for example we have boost::algorithm:: (singular) but boost::operators:: (plural) but we clear cannot use boost::operator::. For such a decision, that is if we go for has_operator_xxx or operators::has_xxx, what is the practice to decide what we accept? Obviously it would be difficult to have everybody satisfied with either method. I do not want to switch to namespace if we are not sure to go for it. Same question for has_new_operator vs has_operator_new vs operators::has_new Frédéric

On 11/18/2010 3:16 AM, Frédéric Bron wrote:
I would not worry about the inconsistency.
I think if we go for a boost::operators namespace we should not worry about tacking the 'operator_' onto the individual names at all, as in your brief enumeration above. OTOH if, like the rest of type_traits, it all goes in the boost namespace, my preference is to use 'has_operator_' as the prefix to all of the operator functionality. John Maddock, whose the type_traits developer, may also have a preference here. I personally would not spend too much time thrashing about it. But it is really your call ultimately.

Edward Diener wrote:
There is precedent: boost::tuples, for example.
For such a decision, that is if we go for has_operator_xxx or operators::has_xxx, what is the practice to decide what we accept?
Discussion on the list and then, as Edward noted, get John Maddock's approval.
+1
+1 _____ 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.

Nevin Liber wrote:
boost::operators::has_less_than boost::has_operator_less_than The "penalty" in a header, assuming no typedefs or using directives in class definitions or in template or inline function definitions, is two extra characters. In those contexts where a using directive is not a problem, the advantage is nine characters. I think the namespace wins pretty handily in that equation. Obviously, one isn't too likely to use many such traits in any given inline or template function definition, so a using directive to use just one means an even worse penalty: "using namespace boost::operators; has_less_than" has 18 more characters than does "boost::has_operator_less_than." Thus, the namespace loses significantly in that comparison. Add the use of a second trait and the balance tips in favor of the namespace again. There is, however, one additional benefit to the namespace approach: dealing with boost::has_new_operator. We can keep has_new_operator as a vestigial and even deprecated oddity and put the rest, including has_new, in the operators namespace. Thus, folks will be encouraged to use boost::operators::has_new due to its consistency with all of the rest. (One might even make has_new_operator hard to find in the documentation to further the adoption of the other.) _____ 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.

boost::operators::has_less_than boost::has_operator_less_than
If we go for the namespace version, what about putting all header files in a subdirectory of typetraits, i.e. boost/type_traits/operators/has_xxx.hpp? Otherwise, we have to keep "operator" in the file name... Frédéric

Frédéric Bron wrote:
Definitely. That, of course, also implies that "type_traits" should be a namespace name, as John pointed out. _____ 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.

Edward Diener wrote:
+1 _____ 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.

Edward Diener-3 wrote:
I prefer has_division_operator than has_operator_division, and yet more than has_operator_divide. division_operator ::= operator/()
Note that interfaces must be designed not only to satisfy the developer but to improve the readability. If a namespace is used, as a user I will need to be able to decipher that has_division<T>::type intends to be "has division operator". I will find much more clear If I have to read has_division_operator<T>::type IMO, namespaces in this case doesn't make the code more readable. Best, Vicenrte -- View this message in context: http://boost.2283326.n4.nabble.com/review-request-addition-to-type-traits-li... Sent from the Boost - Dev mailing list archive at Nabble.com.

My apologies for coming late to this discussion, I appear to have reached my "bandwidth limit" for the max number of simultaneous discussions - not that that's all that hard :-( A couple of observations: * I'll go with the consensus on naming, and if that means changing the names of existing traits, then so be it - as long as the names that are part of the std draft remain unchanged. * Personally I prefer either has_XXX_operator or has_operator_XXX to the namespace approach. The latter has the advantage of grouping all the operator test names together in the alphabetical index, the former reads easier and more descriptively to me (we can always add a new section to the "type types by category" section of docs). But <shrug> I guess. * I'm not that keen on the operators:: subnamespace, to me that namespace is reserved for the operators library http://www.boost.org/doc/libs/1_45_0/libs/utility/operators.htm it would have to be boost::type_traits::operators I guess which is rather less cute :-( * If boost::is_convertible doesn't work as required for this library, then we should fix it, not reinvent it. We already have a lot of compiler workarounds and acumulated knowledge in that version, it would be a shame to loose that by reinventing the wheel. Other than that, it would be really good to finally see comprehensive operator testing in type_traits. Regards, John.

On 11/20/2010 5:01 AM, John Maddock wrote:
I personally feel this should be avoided because of it confusing and irritating end-users who already may be using any current name in their code, as well as differing from the std draft. Unless of course some name were a complete misrepresentation but I do not think that is the case discussed.
I forgot about this when I made my suggestion. I myself would not find boost::type_traits::operators to be too onerous, since after all one could alias the namespace as one likes, but I am a much stronger proponent of clarity in reading code than how much I have to type and many others seem to find important how many letters they have to type to use something in their code.
Very much agreed. I want/need these for a possible Boost library I still have held off putting in the sandbox until this was finished. So I will be very personally happy if Frederick Bron's work gets reviewed.

So how do we decide for one or the other solution. What would be the way to decide? As far as I am concerned, I have no strong opinion. The namespace solution looks clearer but I like short typing too... Do we need to organize sort of a vote? Frédéric

The namespace solution can, if you alias the namespace properly, lead to shorter writing... that is the nice thing, the namespace approach should satisfy both groups (those who like shorter names and those who like longer but perhaps more descriptive names). It's a win win, in my opinion. Petr Pilař.

On 11/20/2010 4:15 AM, viboes wrote:
'boost::type_traits::operators::has_division<T>::type' seems readable to me. Or if you think this is too long you could use a namepsace alias and do: namespace tt_operator = boost::type_traits::operators; and then 'tt_operator::has_division<T>::type' is short enough. Surely you have heard of that famous saying, "Readability is in the eye of the developer" <g>.

When several developers work together this is more subtle. One developer could write has_division::type and the others could find this not enough explict. With has_division_operator interface there is no direct possibility of this shortcut. Don't forget tha the code you develop one day, will be maintained most of the time by others later. Best, Vicente

viboes wrote: ...
...
has_division<T>::type ...
has_division_operator<T>::type
I assume that for consistency that these all imply multiplication_operator has_multiplication<T>::type has_multiplication_operator<T>::type has_addition_operator<T>::type has_subtraction_operator<T>::type ... Is that correct? Also given the growth of EDSL's and operator overloading in general are these names too domain specific? For example boost::filesystem overloads operator/ for concatenating a path: p / "file.txt". Has thought been given to more agnostic names such as: has_operator_star has_operator_slash has_operator_plus has_operator_minus ... Jeff

Jeff Flinn wrote:
That's an interesting idea, but standardized names for the characters would be necessary. "Star" is colloquial; "asterisk" is more appropriate. Instead of "minus," "hyphen" seems more correct and what about the unary versus binary overloads? Here's a longer list to consider: has_operator_hyphen_unary has_operator_hyphen_binary has_operator_tilde has_operator_exclamation has_operator_percent has_operator_caret (not "circumflex" which is a diacritical) has_operator_ampersand has_operator_asterisk has_operator_pipe has_operator_left_guillemet? has_operator_right_guillemet? Since the guillemets are real characters, and the C++ operators are formed by two characters parsed as one token, using "guillemet" isn't correct. One could also use "chevron" which are sometimes doubled to represent guillemets, but the less-than and greater-than characters are not chevrons. Calling them "left shift" and "right shift" brings one full circle to naming the operators for the operations they represent on built-in types, while "insertion" and "extraction" are related to their use with IOStreams. I don't know what to call those. The contrary view to what you've suggested is to note in the documentation that the operators are named for their use with built-in types and leave it at that. _____ 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.

John Maddock wrote:
That's an interesting idea, but standardized names for the characters would be necessary.
[snip]
That's the response I tried to elicit! _____ 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.

viboes wrote:
This is, perhaps, a native language issue. The order of modifier and modified in natural languages varies. My contention is that "operator xxx" is C++ syntax and is read as "operator xxx" (for the various operators), so that leads to corresponding traits named "has operator xxx" thus "has_operator_xxx."
It's quite readable to me for two reasons. First, I can't think of any other meaning of "has division" than "has a division operator." Second, I will need to know the namespace from which such a type came to understand the code. If I'm reading another's code, I'll want to know about using directives, using declarations, and namespace aliases.
I will find much more clear If I have to read
has_division_operator<T>::type
In situ, without first being cognizant of namespace aliases, using directives, and using declarations, that is ever so slightly more obvious, but "has_division" isn't unclear by any stretch. If "operator" is to be in the trait name, don't forget the new operator versus operator new naming issue. The former is used in new expressions and the latter is the operator one can overload to allocate memory. The correct word order for the latter, from the Standard, is "operator new." Thus, the correct name for that trait is "has_operator_new" and that leads to the same order for the other traits, not to mention the lexicographical grouping that results. John is willing to rename has_new_operator, so we have latitude to consider it. _____ 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.

On 17 November 2010 14:48, Stewart, Robert <Robert.Stewart@sig.com> wrote:
Please don't. Most people will never remember which order it is in. Choice actually makes things worse, as they'll get used interchangeably. Plus, "new operator" and "operator new" mean very different things in C++, and most people don't remember which is which. I'd rather have one slight inconsistency. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

Not sure I am able to add all of them. &, * look rather similar to unary operator I added.
BTW, where are the new extensions documented, could you give a link?
Documentation is accessible at libs/type_traits/doc/html/index.html in the archive or in the sandbox. Frédéric

----- Original Message ----- From: "Frédéric Bron" <frederic.bron@m4x.org> To: <boost@lists.boost.org> Sent: Wednesday, November 17, 2010 9:23 PM Subject: Re: [boost] review request: addition to type_traits libraryofhas_operator_xxx
Not sure I am able to add all of them. &, * look rather similar to unary operator I added.
BTW, where are the new extensions documented, could you give a link?
Documentation is accessible at libs/type_traits/doc/html/index.html in the archive or in the sandbox. Frédéric _______________________________________________ It will be a shame if some operators, for which the trait can be defined, dont have it. Have you take a look to ConceptTraits? Maybe you can adapt the implementation? Oh I see them now on the reference documentation. I was looking for a section that explains the motivation and the list of new traits that need to be reveiwed. Vicente

----- Original Message ----- From: "Frédéric Bron" <frederic.bron@m4x.org> To: <boost@lists.boost.org> Sent: Wednesday, November 17, 2010 9:12 PM Subject: Re: [boost] review request: addition to type_traits library ofhas_operator_xxx Surely << and >> are totally similar and I will add them. Prefix ++ and -- should work just fine also. Postfix ++ and -- need special care due to the additionnal int but this should be rather straitforward. What about +=, -=, *=, /=? Frédéric _______________________________________________ Yes, please, add all of them. See my other post. Vicente

On 11/16/2010 3:49 PM, Frédéric Bron wrote:
I have a few comments on the implementation. On MSVC (at least), you can do a bit better in detecting whether expressions have a void type, even if type of the expression has an overloaded comma operator. However, it doesn't work with GCC (as far as I know), so I don't know if it's of any interest. Let me know. To take care of operators involving *only* builtin types, I dispatch to another metafunction, which might be preferable to listing out all the special cases of combinations of builtin types. Actually, I'm not entirely sure how you cover builtin types. I see in has_operator_bit_and.hpp that you list combinations of builtin types that *don't* have the bitand operator (side note: shouldn't we stick to standard spellings here, e.g., "bitand" over "bit_and"?); is this list suppose to be exhaustive? Other than that, the implementation is essentially identical to my own solution. I still think one should be able to pass a metafunction to an operator trait to be invoked on the actual result type of the operation, but that's more of a feature request than an implementation comment. - Jeff

Could you give an example? I do not understand clearly what you mean. Problem here is that for example (double % double) yields a compile time error and that it is not possible to overload operator%(built-in type, built-in type). So if you have a solution that replaces a long list of template specialization by much fewer lines, please propose.
Yes it is supposed to be exhaustive.
I will first try to comply to all other request, i.e. (i) extension to all operators (already done <<, >> and op=), (ii) use existing is_convertible<T>. Then, maybe you could add your proposal as a later addition. Frédéric

On 11/29/2010 1:53 AM, Frédéric Bron wrote:
For example: template< class T, class U, class Result, bool = boost::is_class< typename boost::remove_reference<T>::type
struct has_operator_random_dispatch; template< class T, class U, class Result > struct has_operator_random_dispatch< T, U, Result, true > { ...either T or U is of a class/union type, so we can implement as before... }; template< class T, class U, class Result > struct has_operator_random_dispatch< T, U, Result, false > : builtin_has_operator_random< T, U, Result > { }; and then one defines builtin_has_operator_random appropriately. For example (not 100% sure this is correct...): template< class T, class U, class Result, class StrippedT = typename boost::remove_const< typename boost::remove_reference<T>::type >::type, class StrippedU = typename boost::remove_const< typename boost::remove_reference<U>::type >::type
struct builtin_is_comparable : boost::mpl::and_< boost::is_arithmetic< StrippedT >, boost::is_arithmetic< StrippedU >, boost::mpl::or_< boost::is_void< Result >, boost::is_convertible< bool, Result > > > { }; template< class T, class U, class Result, class V, class W > struct builtin_is_comparable< T, U, Result, V*, W* > : boost::mpl::and_< boost::mpl::or_< boost::is_convertible< typename boost::remove_cv<V>::type *, typename boost::remove_cv<W>::type * >, boost::is_convertible< typename boost::remove_cv<W>::type *, typename boost::remove_cv<V>::type * > >, boost::mpl::or_< boost::is_void< Result >, boost::is_convertible< bool, Result > > > { };
Where do you cover pointer types? To me, it seems more error prone to try to exhaustively list all the builtin cases, and I think generally you'll need some more advanced logic anyway (e.g., as far as I know, 2 pointer types are comparable iff one is convertible to the other after stripping cv qualifiers of their pointee). On the other hand, it can be difficult to get the logic right for a general metafunction covering all combinations of builtin types...
I don't see operator= in your list of has_operator_*.hpp files... (and I'm curious how you managed to implement that one!)
I found it convenient, in this context, for is_convertible< T, void > to evaluate to true; I would assume that's why you were using your own implementation originally. - Jeff

I made some progress in the implementation of operator traits. 1. as John was not in favor of the namepace option, I sticked to has_operator_xxx naming convention. 2. I have already added the following operators (in the sandbox): +=, *=, -=, /*, %=, &=, |=, ^=, <<, >>, >>=, <<= 3. I have tried to use the existing is_convertible traits but without any success: The problem is that is_convertible expect a type as parameter, not an expression. So I have tried to get the return type of the operator with: a) boost result_of (but this does not work for arbitrary expression), b)boost typeof (but types must be registered first) c) with the following code: #include <iostream> #include <boost/type_traits/is_convertible.hpp> template <class To, class From> bool is_conv(const From&) { return boost::is_convertible< From, To >::value; }; struct tag { }; int main() { std::cout<<is_conv<bool>(1+2)<<'\n'; std::cout<<is_conv<tag>(1+2)<<'\n'; return 0; } but if I use is_conv(1+2) to set a value variable of type bool in the traits, the compiler complains that "is_conv(1+2)" is not a constant expression... So my questions: 1. is there an easy way to get the type of an expression and to pass it to is_convertible 2. if not, why not just using the working code I wrote already? Regards, Frédéric

On 12/9/2010 12:18 PM, Frédéric Bron wrote:
Here, rather than returning a run-time true/false, return a compile-time true/false. I.e., use the sizeof trick. template< std::size_t N > struct sizeof_t { char _[N]; }; BOOST_MPL_ASSERT_RELATION( sizeof( sizeof_t<1> ), !=, sizeof( sizeof_t<2> ) ); template< class To, class From > sizeof_t< boost::is_convertible< const From&, To >::value + 1 > is_conv(const From&); [...]
So now sizeof( is_conv< bool >(1+2) ) == sizeof( sizeof_t<2> ) is a static constant expression. It could also be useful to distinguish the const-qualifications of referents, i.e., add another overload of is_conv: template< class To, class From > sizeof_t< boost::is_convertible< From&, To >::value + 1 > is_conv(From&); And you can get even fancier if you want to distinguish between lvalues and rvalues (warning: untested): template< class From, class T > struct is_conv_result { typedef sizeof_t< boost::is_convertible< From, To >::value + 1 > type; }; template< class To, class From1, class From2 > typename is_conv_result< From1&, To >::type is_conv(From1&, From2&); // catches lvalue references-to-non-const template< class To, class From1, class From2 > typename is_conv_result< const From1&, To >::type is_conv(const From1&, From2&); // catches lvalue references-to-const template< class To, class From > typename is_conv_result< From, To >::type is_conv(const From&, ...); // catches rvalues You just need to pass the expression in question to both parameters of is_conv. HTH, - Jeff

1. is there an easy way to get the type of an expression and to pass it to is_convertible
Not in C++03, only in C++0x
2. if not, why not just using the working code I wrote already?
That sounds sensible - I may have misunderstood here - I thought from the comments I saw that you had written your own is_convertible version, but rather it looks like you have some implementation details (functions) that just happen to have a similar name? If so I suggest you leave it as it is - sorry for the confusion on that one! John.
participants (14)
-
Alexander Fokin
-
Edward Diener
-
Frédéric Bron
-
Jeff Flinn
-
Jeffrey Lee Hellrung, Jr.
-
joel falcou
-
Joel Falcou
-
John Maddock
-
Nevin Liber
-
Petr Pilař
-
Stewart, Robert
-
viboes
-
Vicente BOTET
-
vicente.botet