[TypeTraits] propose new callable-type call traits
I've implemented a few traits classes (code attached below) that, for a given callable-type T, and a set of Args..., detects whether the function call T::operator()(Args...) would be
(1) valid,
(2) ambiguous, or
(3) none is viable.
where T is allowed to have arbitrary overloaded operator()'s.
This could be useful for programmers who want to
(1) make the function call only when it's valid,
(2) generate customized error/warning messages when it's ambiguous, or handle/redirect it without getting an ambiguous compiler error, and
(3) redirect non-viable calls to some default behavior, which can be different than how it's handled when the call is ambiguous.
If people are interested, I suppose this could be a useful addition to TypeTraits.
I would appreciate any feedback!
For clarity, the implementation and demonstration attached below is written in c++11. It can be easily ported to c++98/03 compilers by using TypeTratis to replace the std type traits, BOOST_TYPEOF_KEYWORD to replace decltype, and Boost.Preprocessor to generate code for arbitrary arity.
- Hui Li
#include <utility>
#include
Hi,
2014/1/12 Hui Li
I've implemented a few traits classes (code attached below) that, for a given callable-type T, and a set of Args..., detects whether the function call T::operator()(Args...) would be (1) valid, (2) ambiguous, or (3) none is viable. where T is allowed to have arbitrary overloaded operator()'s.
This could be useful for programmers who want to (1) make the function call only when it's valid, (2) generate customized error/warning messages when it's ambiguous, or handle/redirect it without getting an ambiguous compiler error, and (3) redirect non-viable calls to some default behavior, which can be different than how it's handled when the call is ambiguous.
If people are interested, I suppose this could be a useful addition to TypeTraits. I would appreciate any feedback!
For clarity, the implementation and demonstration attached below is written in c++11. It can be easily ported to c++98/03 compilers by using TypeTratis to replace the std type traits, BOOST_TYPEOF_KEYWORD to replace decltype, and Boost.Preprocessor to generate code for arbitrary arity.
It's interesting that you use ambiguity to distinguish the types. Anyway, your code doesn't support function(pointer) which is also Callable, and... are you sure your approach can be ported back to C++03 where there's no SFINAE on expression? FWIW, I have my has_call implementation: https://github.com/jamboree/boost.has_call which works for both C++11/03. I have real world usage of has_call (or has_valid_call in yours), but I don't have the need of has_ambiguous_call so far.
On Jan 11, 2014, at 2:55 PM, TONGARI J
Hi,
2014/1/12 Hui Li
I've implemented a few traits classes (code attached below) that, for a given callable-type T, and a set of Args..., detects whether the function call T::operator()(Args...) would be (1) valid, (2) ambiguous, or (3) none is viable. where T is allowed to have arbitrary overloaded operator()'s.
This could be useful for programmers who want to (1) make the function call only when it's valid, (2) generate customized error/warning messages when it's ambiguous, or handle/redirect it without getting an ambiguous compiler error, and (3) redirect non-viable calls to some default behavior, which can be different than how it's handled when the call is ambiguous.
If people are interested, I suppose this could be a useful addition to TypeTraits. I would appreciate any feedback!
For clarity, the implementation and demonstration attached below is written in c++11. It can be easily ported to c++98/03 compilers by using TypeTratis to replace the std type traits, BOOST_TYPEOF_KEYWORD to replace decltype, and Boost.Preprocessor to generate code for arbitrary arity.
It's interesting that you use ambiguity to distinguish the types. Anyway, your code doesn't support function(pointer) which is also Callable, and... are you sure your approach can be ported back to C++03 where there's no SFINAE on expression?
SFINAE on expression is not important in my approach. Since the has_compatible_callback merely uses has_valid_call, and my has_valid_call does the same thing as your has_call (besides the return type), i'm quite certain we can use your has_call for it's implementation and should be able to port to whatever compiler that works with your has_call.
FWIW, I have my has_call implementation: https://github.com/jamboree/boost.has_call which works for both C++11/03.
Very interesting, I will look into it, and see if i can switch to your has_call so that mine works on gcc4.3 (which I'm guessing is one of the compilers that don't support SFINAE on expression?)
I have real world usage of has_call (or has_valid_call in yours), but I don't have the need of has_ambiguous_call so far.
I have real world use for has_compatible_call, because has_call was insufficient in detecting ambiguous callback overloads provided by user defined classes (out of my control since I'm writing a library), in which case I want to generate a compiler error rather than using some default handler.
For example,
// in my real world application, there are other make_the_call overloads that make it more interesting than in this demonstration
template < typename E, typename T > // T is some user-defined class outside the library
typename std::enable_if<
has_valid_call
::type make_the_call(const E& e, T& t) { t(e); // if the call is ambiguous, the user must, and will, get a compiler error here // if we had used just has_valid_call in the enable_if, it would turn an erroneous ambiguity into a no-call // which would be very very wrong }
// default no-op behavior
template < typename E, typename T >
typename std::enable_if<
has_no_viable_call
::type make_the_call(const E& e, T& t) { // do nothing }
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Sorry I wasn't very clear in my last email. I want to add that if i only had has_call, and implemented my make_the_call like in the following, it would be very very wrong.
template < typename E, typename T > // T is some user-defined class outside the library
typename std::enable_if<
has_call
::type make_the_call(const E& e, T& t) { t(e); // if the call is ambiguous, the user must, and will, get a compiler error here // and since has_call disable of this overload and enables the next one (see below) // we turn an ambiguity error into a mere no-call, which is very very wrong }
// default no-call behavior, which would be called if T(E) is actually ambiguous
template < typename E, typename T >
typename std::enable_if<
not has_call
::type make_the_call(const E& e, T& t) { // do nothing }
On Jan 11, 2014, at 6:55 PM, Hui Li
On Jan 11, 2014, at 2:55 PM, TONGARI J
wrote: Hi,
2014/1/12 Hui Li
I've implemented a few traits classes (code attached below) that, for a given callable-type T, and a set of Args..., detects whether the function call T::operator()(Args...) would be (1) valid, (2) ambiguous, or (3) none is viable. where T is allowed to have arbitrary overloaded operator()'s.
This could be useful for programmers who want to (1) make the function call only when it's valid, (2) generate customized error/warning messages when it's ambiguous, or handle/redirect it without getting an ambiguous compiler error, and (3) redirect non-viable calls to some default behavior, which can be different than how it's handled when the call is ambiguous.
If people are interested, I suppose this could be a useful addition to TypeTraits. I would appreciate any feedback!
For clarity, the implementation and demonstration attached below is written in c++11. It can be easily ported to c++98/03 compilers by using TypeTratis to replace the std type traits, BOOST_TYPEOF_KEYWORD to replace decltype, and Boost.Preprocessor to generate code for arbitrary arity.
It's interesting that you use ambiguity to distinguish the types. Anyway, your code doesn't support function(pointer) which is also Callable, and... are you sure your approach can be ported back to C++03 where there's no SFINAE on expression?
SFINAE on expression is not important in my approach. Since the has_compatible_callback merely uses has_valid_call, and my has_valid_call does the same thing as your has_call (besides the return type), i'm quite certain we can use your has_call for it's implementation and should be able to port to whatever compiler that works with your has_call.
FWIW, I have my has_call implementation: https://github.com/jamboree/boost.has_call which works for both C++11/03.
Very interesting, I will look into it, and see if i can switch to your has_call so that mine works on gcc4.3 (which I'm guessing is one of the compilers that don't support SFINAE on expression?)
I have real world usage of has_call (or has_valid_call in yours), but I don't have the need of has_ambiguous_call so far.
I have real world use for has_compatible_call, because has_call was insufficient in detecting ambiguous callback overloads provided by user defined classes (out of my control since I'm writing a library), in which case I want to generate a compiler error rather than using some default handler.
For example,
// in my real world application, there are other make_the_call overloads that make it more interesting than in this demonstration
template < typename E, typename T > // T is some user-defined class outside the library typename std::enable_if< has_valid_call
::value or has_ambiguous_call ::value ::type make_the_call(const E& e, T& t) { t(e); // if the call is ambiguous, the user must, and will, get a compiler error here // if we had used just has_valid_call in the enable_if, it would turn an erroneous ambiguity into a no-call // which would be very very wrong }
// default no-op behavior template < typename E, typename T > typename std::enable_if< has_no_viable_call
::value ::type make_the_call(const E& e, T& t) { // do nothing }
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
I tried your boost::has_call. Unfortunately it doesn't compile when the call for the given type and signature is ambiguous.
Did you intend that it'd be false_type in that case (since you did seem to suggest that your has_call behave similarly to my has_valid_call) ?
Submitted an issue on github: https://github.com/jamboree/boost.has_call/issues/1 .
On Jan 11, 2014, at 2:55 PM, TONGARI J
Hi,
2014/1/12 Hui Li
I've implemented a few traits classes (code attached below) that, for a given callable-type T, and a set of Args..., detects whether the function call T::operator()(Args...) would be (1) valid, (2) ambiguous, or (3) none is viable. where T is allowed to have arbitrary overloaded operator()'s.
This could be useful for programmers who want to (1) make the function call only when it's valid, (2) generate customized error/warning messages when it's ambiguous, or handle/redirect it without getting an ambiguous compiler error, and (3) redirect non-viable calls to some default behavior, which can be different than how it's handled when the call is ambiguous.
If people are interested, I suppose this could be a useful addition to TypeTraits. I would appreciate any feedback!
For clarity, the implementation and demonstration attached below is written in c++11. It can be easily ported to c++98/03 compilers by using TypeTratis to replace the std type traits, BOOST_TYPEOF_KEYWORD to replace decltype, and Boost.Preprocessor to generate code for arbitrary arity.
It's interesting that you use ambiguity to distinguish the types. Anyway, your code doesn't support function(pointer) which is also Callable, and... are you sure your approach can be ported back to C++03 where there's no SFINAE on expression?
FWIW, I have my has_call implementation: https://github.com/jamboree/boost.has_call which works for both C++11/03.
I have real world usage of has_call (or has_valid_call in yours), but I don't have the need of has_ambiguous_call so far.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
2014/1/12 Hui Li
I tried your boost::has_call. Unfortunately it doesn't compile when the call for the given type and signature is ambiguous. Did you intend that it'd be false_type in that case (since you did seem to suggest that your has_call behave similarly to my has_valid_call) ?
Yes.
Submitted an issue on github: https://github.com/jamboree/boost.has_call/issues/1 .
I think it's a limitation in C++03 anyway, since to avoid the ambiguity you need SFINAE on expr, or do you have any idea? You raised an interesting issue I haven't thought of, but is it really needed to distinguish between no-viable-call and ambiguous-call?
On 1/11/2014 11:05 AM, Hui Li wrote:
I've implemented a few traits classes (code attached below) that, for a given callable-type T, and a set of Args..., detects whether the function call T::operator()(Args...) would be (1) valid, (2) ambiguous, or (3) none is viable. where T is allowed to have arbitrary overloaded operator()'s. <snip>
Just FYI: https://svn.boost.org/trac/boost/ticket/3783 -- Eric Niebler Boost.org http://www.boost.org
Has is_callable_with_args made into boost somewhere? Or is there a plan to make it into boost? Would like to know the status since the link is a few years old.
It appears all 3 versions we have here follow 2 different approaches, and are different in terms of how an ambiguous call is handled.
(1) compute and compare sizeof(<function call expression>) in some form
This includes Eric Niebler's is_callable_with_args, and boost::has_call in c++03/98.
In this approach, an ambiguous call would result in a compiler error within the traits class.
(2) decltype(<function call expression>) in SFINAE context
This includes my has_valid_call, and boost::has_call in c++11.
In this approach, an ambiguous call would merely get the trait class evaluated to a false-type.
Whether we want to distinguish non-viable and ambiguous calls or not, i think boost::has_call should behave the same in c++03/98 and in c++11. The current version does not, which can be surprising to users.
I no longer have a real world application where distinguishing a non-viable call and ambiguous call is absolutely necessary (for my use case, i'm going to switch to the sizeof approach to get the compile error for ambiguous calls).
However, I do think it's worth exposing to programmers as much information as possible of whatever the compiler knows. Since the compiler can distinguish a non-viable call and an ambiguous call, the programmers should be able to do that too,
so that they have more control. that's one of the reason that we have metaprogramming, isn't it? :)
On Jan 12, 2014, at 12:24 AM, Eric Niebler
On 1/11/2014 11:05 AM, Hui Li wrote:
I've implemented a few traits classes (code attached below) that, for a given callable-type T, and a set of Args..., detects whether the function call T::operator()(Args...) would be (1) valid, (2) ambiguous, or (3) none is viable. where T is allowed to have arbitrary overloaded operator()'s. <snip>
Just FYI: https://svn.boost.org/trac/boost/ticket/3783
-- Eric Niebler Boost.org http://www.boost.org
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
2014/1/12 Hui Li
Has is_callable_with_args made into boost somewhere? Or is there a plan to make it into boost? Would like to know the status since the link is a few years old.
It appears all 3 versions we have here follow 2 different approaches, and are different in terms of how an ambiguous call is handled.
(1) compute and compare sizeof(<function call expression>) in some form This includes Eric Niebler's is_callable_with_args, and boost::has_call in c++03/98. In this approach, an ambiguous call would result in a compiler error within the traits class.
(2) decltype(<function call expression>) in SFINAE context This includes my has_valid_call, and boost::has_call in c++11. In this approach, an ambiguous call would merely get the trait class evaluated to a false-type.
Whether we want to distinguish non-viable and ambiguous calls or not, i think boost::has_call should behave the same in c++03/98 and in c++11. The current version does not, which can be surprising to users.
You may be right on providing the consistent behavior, but I don't know if it'd be an oversight to add this restriction to C++11. I no longer have a real world application where distinguishing a non-viable
call and ambiguous call is absolutely necessary (for my use case, i'm going to switch to the sizeof approach to get the compile error for ambiguous calls).
Most of my usage will turn into error eventually no matter it's ambiguous or non-viable, so it's non-issue to me thus far.
However, I do think it's worth exposing to programmers as much information as possible of whatever the compiler knows. Since the compiler can distinguish a non-viable call and an ambiguous call, the programmers should be able to do that too, so that they have more control. that's one of the reason that we have metaprogramming, isn't it? :)
Anyway, feel free to propose those to Boost, and don't hesitate to pick up the code/doc/test ready on my github if appropriate. I'd like to see this functionality goes into Boost :)
I've implemented a few traits classes (code attached below) that, for a given callable-type T, and a set of Args..., detects whether the function call T::operator()(Args...) would be (1) valid, (2) ambiguous, or (3) none is viable. where T is allowed to have arbitrary overloaded operator()'s.
This could be useful for programmers who want to (1) make the function call only when it's valid, (2) generate customized error/warning messages when it's ambiguous, or handle/redirect it without getting an ambiguous compiler error, and (3) redirect non-viable calls to some default behavior, which can be different than how it's handled when the call is ambiguous.
If people are interested, I suppose this could be a useful addition to TypeTraits. I would appreciate any feedback!
For clarity, the implementation and demonstration attached below is written in c++11. It can be easily ported to c++98/03 compilers by using TypeTratis to replace the std type traits, BOOST_TYPEOF_KEYWORD to replace decltype, and Boost.Preprocessor to generate code for arbitrary arity.
I think that might make an excellent addition to type_traits - however, I have no time at present to do anything on this myself, so you would have to provide a complete solution, including docs and tests etc. As others have mentioned, reliance on SFINAE-expressions is an issue because msvc still doesn't fully support this. Other than that, we probably should have one eye towards programming in C++11 these days... John.
On Jan 12, 2014, at 4:54 AM, John Maddock
I think that might make an excellent addition to type_traits - however, I have no time at present to do anything on this myself, so you would have to provide a complete solution, including docs and tests etc.
I would provide a complete solution including docs and tests. But i will have to learn how to do things the boost way since this would be my first contribute. Would appreciate if anyone could point me to some general guidelines and maybe also on for example how to work with boost Github etc.
As others have mentioned, reliance on SFINAE-expressions is an issue because msvc still doesn't fully support this.
As for SFINAE-expression, I agree with Tongari J. that it seems necessary to be able to distinguish ambiguous and non-viable calls. Do you maybe have any ideas for workaround? Is it unacceptable if it doesn't support those compilers like msvc?
Other than that, we probably should have one eye towards programming in C++11 these days...
I'm not sure if my implementation should have the same user interface as boost::has_call (since it came to existence first).
I suppose mine could've looked like this:
1. has_valid_call
John.
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
2014/1/12 Hui Li
On Jan 12, 2014, at 4:54 AM, John Maddock
wrote: I think that might make an excellent addition to type_traits - however, I have no time at present to do anything on this myself, so you would have to provide a complete solution, including docs and tests etc.
I would provide a complete solution including docs and tests. But i will have to learn how to do things the boost way since this would be my first contribute. Would appreciate if anyone could point me to some general guidelines and maybe also on for example how to work with boost Github etc.
As others have mentioned, reliance on SFINAE-expressions is an issue because msvc still doesn't fully support this.
As for SFINAE-expression, I agree with Tongari J. that it seems necessary to be able to distinguish ambiguous and non-viable calls. Do you maybe have any ideas for workaround? Is it unacceptable if it doesn't support those compilers like msvc?
Other than that, we probably should have one eye towards programming in C++11 these days...
I'm not sure if my implementation should have the same user interface as boost::has_call (since it came to existence first). I suppose mine could've looked like this: 1. has_valid_call
// same as boost::has_call 2. has_ambiguous_call // R is ignored 3. has_no_viable_call // R is ignored Any thoughts? Or maybe i could extend boost::has_call by adding has_ambiguous_call
and has_no_viable_call (for c++11 only though). I think it may be better than providing another standalone implementation that has overlapping functionalities with boost::has_call.
Note that boost::has_call is *not* part of official Boost, it's still my personal work, so feel free to do whatever you want. As I mentioned, you could take the doc/test/code on my github :)
On Jan 12, 2014, at 9:27 AM, TONGARI J
On Jan 12, 2014, at 4:54 AM, John Maddock
wrote: Note that boost::has_call is *not* part of official Boost, it's still my personal work, so feel free to do whatever you want.
Right, but what's the status on becoming official? Is it still being reviewed? is there a plan to make it official at some point?
As I mentioned, you could take the doc/test/code on my github :) _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
2014/1/13 Hui Li
On Jan 12, 2014, at 9:27 AM, TONGARI J
wrote: On Jan 12, 2014, at 4:54 AM, John Maddock
wrote:
Note that boost::has_call is *not* part of official Boost, it's still my personal work, so feel free to do whatever you want.
Right, but what's the status on becoming official? Is it still being reviewed?
No. I just advertised this to the community and tried to gather interest sometime ago, but didn't go for further review process. is there a plan to make it official at some point?
Well, doc/test are there, but no certain plan yet, I haven't got the time to try out modular Boost myself. So, please go ahead, it'd be great if you could bring this into Boost eventually.
On Jan 12, 2014, at 12:11 PM, TONGARI J
No. I just advertised this to the community and tried to gather interest sometime ago, but didn't go for further review process.
Well, doc/test are there, but no certain plan yet, I haven't got the time to try out modular Boost myself. So, please go ahead, it'd be great if you could bring this into Boost eventually.
I see. I'll look into modular boost.
Here is my proposal to extend your boost::has_call.
Please let me know what you think, and I'd love to hear feedback from anyone.
1. Guard the current limited has_call implementation with #ifdef BOOST_NO_SFINAE_EXPR.
2. when sfinae-expr is supported, implement has_call based on sfinae expression (my approach or something similar).
This would make has_call evaluate to false when the call is ambiguous on c++03 compilers that support sfinae-expr, which makes it closer to your original intent.
3. add has_ambiguous_call
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
2014/1/13 Hui Li
On Jan 12, 2014, at 12:11 PM, TONGARI J
wrote: No. I just advertised this to the community and tried to gather interest sometime ago, but didn't go for further review process.
Well, doc/test are there, but no certain plan yet, I haven't got the time to try out modular Boost myself. So, please go ahead, it'd be great if you could bring this into Boost eventually.
I see. I'll look into modular boost. Here is my proposal to extend your boost::has_call. Please let me know what you think, and I'd love to hear feedback from anyone.
1. Guard the current limited has_call implementation with #ifdef BOOST_NO_SFINAE_EXPR.
2. when sfinae-expr is supported, implement has_call based on sfinae expression (my approach or something similar). This would make has_call evaluate to false when the call is ambiguous on c++03 compilers that support sfinae-expr, which makes it closer to your original intent.
3. add has_ambiguous_call
to both the unlimited implementation, and to the limited implementation when sfinae-expr is available. The return-type R is ignored. 4. add has_no_viable_call
to both the unlimited implementation, and to the limited when sfinae-expr is available, which always evaluate to has_no_viable_call ::value == ! has_call ::value && ! has_ambiguous_call ::value note that this means it evaluates to false when the decltype(T(Args...)) is not convertible to R, which i think is the correct behavior. also note that, for any given , one and only one of the three traits would evaluate to true. 5. to achieve some degree of consistency when there is no sfinae-expr support, we should have (by definition if you like), has_ambiguous_call
::value == false; // because has_call does not compile when ambiguous has_no_viable_call ::value == ! has_call ::value
I'll explain some of my original design rationale here:
1) Name choosing:
"has_call" was originally called "can_be_called", and was intended to be a
separate lib than TypeTraits, but as someone pointed out, TypeTraits seems
a good place, and since there are already Operator Type Traits which is
called has_xxx, I just followed the convention.
2) Return Type Specifying:
other Operator Type Traits let you specify the expected return type, so I
followed, though with a bit different syntax.
But since you also tried to provide more than has_call, I'm not sure if
these apply to the others.
For example, has_ambiguous_call doesn't have to follow the convention as
has_call as you want to ignore the return type, maybe you can make it
is_ambiguous
On 1/12/2014 8:29 AM, Hui Li wrote:
On Jan 12, 2014, at 4:54 AM, John Maddock
wrote: I think that might make an excellent addition to type_traits - however, I have no time at present to do anything on this myself, so you would have to provide a complete solution, including docs and tests etc.
I would provide a complete solution including docs and tests. But i will have to learn how to do things the boost way since this would be my first contribute. Would appreciate if anyone could point me to some general guidelines and maybe also on for example how to work with boost Github etc.
Start here for modular-boost: https://svn.boost.org/trac/boost/wiki/ModularBoost
participants (5)
-
Edward Diener
-
Eric Niebler
-
Hui Li
-
John Maddock
-
TONGARI J