Proposal for 'is_dereferenceable' and other template metafunctions

Hello, A while back, I proposed a template metafunction called 'is_iterator', but the consensus seemed to be that it was not feasible from an implementation standpoint. At Jonathan Turkanis' suggestion, I went ahead and implemented the less ambitions 'is_dereferenceable', along with two other metafunctions: Requirements: Given t of type T &, if the expression *t is not well-formed, then it must neither be ambiguous nor violate access. Description: is_lvalue_dereferenceable<T> - true if *t is well-formed and an lvalue is_value_dereferenceable<T> - true if *t is well-formed and non-void is_dereferenceable<T> - true if *t is well-formed I would like to offer these up for inclusion in boost, whether in namespace boost or in boost::detail. (presumably the latter, since that's where the similar 'is_incrementable' is found). I have posted the source for these metafunctions along with a unit test in the YahooGroups Files section in a folder called 'is_dereferenceable'. Please let me know if these are of interest to anyone. Alex Chovanec

"Alex Chovanec" <achovane@engin.umich.edu> writes:
Hello,
A while back, I proposed a template metafunction called 'is_iterator', but the consensus seemed to be that it was not feasible from an implementation standpoint. At Jonathan Turkanis' suggestion, I went ahead and implemented the less ambitions 'is_dereferenceable', along with two other metafunctions:
Requirements:
Given t of type T &, if the expression *t is not well-formed, then it must neither be ambiguous nor violate access.
Description:
is_lvalue_dereferenceable<T> - true if *t is well-formed and an lvalue is_value_dereferenceable<T> - true if *t is well-formed and non-void is_dereferenceable<T> - true if *t is well-formed
I would like to offer these up for inclusion in boost, whether in namespace boost or in boost::detail. (presumably the latter, since that's where the similar 'is_incrementable' is found).
I have posted the source for these metafunctions along with a unit test in the YahooGroups Files section in a folder called 'is_dereferenceable'.
Please let me know if these are of interest to anyone.
Don't you think it's time we generalized the hack into a nice macro that generates the right code? -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:ullfibawd.fsf@boost-consulting.com...
Don't you think it's time we generalized the hack into a nice macro that generates the right code?
Agreed. That way we could have utilities like these to check for all unary operators. In fact, I don't see any reason why the same idea couldn't be applied to binary operators. I would think that the usage would look something like this: DEFINE_UNARY_OPERATOR_TEST(is_dereferenceable, *); DEFINE_BINARY_OPERATOR_TEST(is_addable, +); Then you could do is_dereferenceable<T> or is_addable<T1, T2> where the latter expression is true if 't1 + t2' is well-formed, where 't1' is an instance of 'T1 &' and 't2' is an instance of 'T2 &'. Is this what you had in mind, or close to it? -- Alex Chovanec NIF and Engineering Division Computing Applications and Research Department Lawrence Livermore National Laboratory

"David Abrahams" <dave@boost-consulting.com> wrote in message news:ullfibawd.fsf@boost-consulting.com...
"Alex Chovanec" <achovane@engin.umich.edu> writes:
I have posted the source for these metafunctions along with a unit test in the YahooGroups Files section in a folder called 'is_dereferenceable'.
Please let me know if these are of interest to anyone.
Don't you think it's time we generalized the hack into a nice macro that generates the right code?
I think so. In addition to prefix and postfix unary operators, there should be a binary version, too, right? Jonathan

Jonathan Turkanis wrote:
"David Abrahams" <dave@boost-consulting.com> wrote in message
Don't you think it's time we generalized the hack into a nice macro that generates the right code?
I think so. In addition to prefix and postfix unary operators, there should be a binary version, too, right?
I have been toying with a more general solution to this problem, where one can do things like: check_dereference<T, is_convertible<mpl::_, U&> > check_add<T, T, is_convertible<mpl::_, T> > etc. I haven't used it anywhere yet, so I can't say if it's useful in practice, but at least it was fun implementing. ;) -- Daniel Wallin

Daniel Wallin <dalwan01@student.umu.se> writes:
Jonathan Turkanis wrote:
"David Abrahams" <dave@boost-consulting.com> wrote in message
Don't you think it's time we generalized the hack into a nice macro that generates the right code? I think so. In addition to prefix and postfix unary operators, there should be a binary version, too, right?
I have been toying with a more general solution to this problem, where one can do things like:
check_dereference<T, is_convertible<mpl::_, U&> > check_add<T, T, is_convertible<mpl::_, T> >
etc.
That *might* be impressive if I could tell what those expressions were supposed to mean ;-)
I haven't used it anywhere yet, so I can't say if it's useful in practice, but at least it was fun implementing. ;)
I'm sure! -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Daniel Wallin <dalwan01@student.umu.se> writes:
I have been toying with a more general solution to this problem, where one can do things like:
check_dereference<T, is_convertible<mpl::_, U&> > check_add<T, T, is_convertible<mpl::_, T> >
etc.
That *might* be impressive if I could tell what those expressions were supposed to mean ;-)
The metafunction class is applied to the result type of the operation, and is required to have boolean result. For instance: struct X { void operator+(X const&) const; X operator+(int) const; }; template<class T> struct Y : mpl::false_ {}; template<> struct Y<X> : mpl::true_ {}; check_add<X, X, Y<mpl::_> >::type // X + X, invokes Y<void> -> false check_add<X, int, Y<mpl::_> >::type // X + int, invoked Y<X> -> true Using this we could express more complicated concept checks. For instance: "The Lvalue Iterator concept adds the requirement that the return type of operator* type be a reference to the value type of the iterator." template<class Iterator> struct is_iterator_lvalue_dereferencable : check_dereference< Iterator , is_same< mpl::_ , typename iterator_value<Iterator>::type& > > { }; -- Daniel Wallin

Daniel Wallin <dalwan01@student.umu.se> writes:
David Abrahams wrote:
Daniel Wallin <dalwan01@student.umu.se> writes:
I have been toying with a more general solution to this problem, where one can do things like:
check_dereference<T, is_convertible<mpl::_, U&> > check_add<T, T, is_convertible<mpl::_, T> >
etc. That *might* be impressive if I could tell what those expressions were supposed to mean ;-)
The metafunction class
I don't see a metafunction class here. Don't you mean "lambda expression?"
is applied to the result type of the operation, and is required to have boolean result. For instance:
struct X { void operator+(X const&) const; X operator+(int) const; };
template<class T> struct Y : mpl::false_ {}; template<> struct Y<X> : mpl::true_ {};
check_add<X, X, Y<mpl::_> >::type // X + X, invokes Y<void> -> false check_add<X, int, Y<mpl::_> >::type // X + int, invoked Y<X> -> true
Using this we could express more complicated concept checks. For instance:
"The Lvalue Iterator concept adds the requirement that the return type of operator* type be a reference to the value type of the iterator."
template<class Iterator> struct is_iterator_lvalue_dereferencable : check_dereference< Iterator , is_same< mpl::_ , typename iterator_value<Iterator>::type& > > { };
Oh, very nice! Now it's impressive. :-) -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Daniel Wallin <dalwan01@student.umu.se> writes:
David Abrahams wrote:
Daniel Wallin <dalwan01@student.umu.se> writes:
I have been toying with a more general solution to this problem, where one can do things like:
check_dereference<T, is_convertible<mpl::_, U&> > check_add<T, T, is_convertible<mpl::_, T> >
etc.
That *might* be impressive if I could tell what those expressions were supposed to mean ;-)
The metafunction class
I don't see a metafunction class here. Don't you mean "lambda expression?"
Maybe. I was trying to describe the formal argument. So I guess something like: template<class T, class U, class Predicate> struct check_add; Where Predicate is a unary metafunction class or a lambda expression. ?? [snip]
Oh, very nice! Now it's impressive. :-)
:) So, anyone interested in this? -- Daniel Wallin

Daniel Wallin <dalwan01@student.umu.se> writes:
David Abrahams wrote:
Daniel Wallin <dalwan01@student.umu.se> writes:
David Abrahams wrote:
Daniel Wallin <dalwan01@student.umu.se> writes:
I have been toying with a more general solution to this problem, where one can do things like:
check_dereference<T, is_convertible<mpl::_, U&> > check_add<T, T, is_convertible<mpl::_, T> >
etc.
That *might* be impressive if I could tell what those expressions were supposed to mean ;-)
The metafunction class I don't see a metafunction class here. Don't you mean "lambda expression?"
Maybe. I was trying to describe the formal argument. So I guess something like:
template<class T, class U, class Predicate> struct check_add;
Where Predicate is a unary metafunction class or a lambda expression.
??
The new definition of "lambda expression" is: a metafunction class -or- a placeholder expression so, you can just say "unary lambda expression."
[snip]
Oh, very nice! Now it's impressive. :-)
:) So, anyone interested in this?
Yeah, sure, I'm interested. I still hope whoever's doing this won't overlook free functions like swap ;-). -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Yeah, sure, I'm interested. I still hope whoever's doing this won't overlook free functions like swap ;-).
I checked it in to the sandbox. boost/result_check.hpp boost/libs/utility/test/result_check_test.cpp It's missing a simple way to somehow specialize for fundamental types.. check_swap<> can be written: BOOST_RESULT_CHECK(2, swap, swap, swap(_1, _2)) arity ---^ ^ ^ ^ trait name --------' | | overload name --------------' | expression --------------------' -- Daniel Wallin

Daniel Wallin <dalwan01@student.umu.se> writes:
David Abrahams wrote:
Yeah, sure, I'm interested. I still hope whoever's doing this won't overlook free functions like swap ;-).
I checked it in to the sandbox.
boost/result_check.hpp boost/libs/utility/test/result_check_test.cpp
It's missing a simple way to somehow specialize for fundamental types..
I don't understand why you'd want that. But maybe I don't understand what "that" is.
check_swap<> can be written:
BOOST_RESULT_CHECK(2, swap, swap, swap(_1, _2)) arity ---^ ^ ^ ^ trait name --------' | | overload name --------------' | expression --------------------'
-- Daniel Wallin
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- Dave Abrahams Boost Consulting http://www.boost-consulting.com

David Abrahams wrote:
Daniel Wallin <dalwan01@student.umu.se> writes:
It's missing a simple way to somehow specialize for fundamental types..
I don't understand why you'd want that. But maybe I don't understand what "that" is.
It's necessary to specialize the trait classes to handle fundamental types, for instance: check_add<float, int*> Would trigger a compilation error otherwise. -- Daniel Wallin

Daniel Wallin <dalwan01@student.umu.se> writes:
David Abrahams wrote:
Daniel Wallin <dalwan01@student.umu.se> writes:
It's missing a simple way to somehow specialize for fundamental types.. I don't understand why you'd want that. But maybe I don't understand what "that" is.
It's necessary to specialize the trait classes to handle fundamental types, for instance:
check_add<float, int*>
Would trigger a compilation error otherwise.
Gotcha. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

Daniel Wallin wrote:
It's necessary to specialize the trait classes to handle fundamental types, for instance:
check_add<float, int*>
Would trigger a compilation error otherwise.
#include <boost/mpl/if.hpp> #include <boost/type_traits/is_fundamental.hpp> #include <boost/preprocessor/arithmetic/dec.hpp> // adding this in namespace boost::result_check_: template<class T> struct boxed_fundamental { operator T (); }; template <class T> struct make_arg { typedef typename add_reference< typename mpl::if_< is_fundamental<T> , boxed_fundamental<T> , T >::type >::type type; }; // changing the RESULT_CHECK_LOCAL_ARG macro to this: #define BOOST_RESULT_CHECK_LOCAL_ARG(z, n, text) \ static typename make_arg<T##n>::type \ BOOST_PP_CAT(_, BOOST_PP_INC(n)); // finally, weakening fallback overload within the // BOOST_RESULT_CHECK macro no_overload_tag function( \ BOOST_PP_ENUM_PARAMS(BOOST_PP_DEC(arity) \ , any BOOST_PP_INTERCEPT), ...); \ // ...should work

Oops... Have to correct / annotade my code:
, any BOOST_PP_INTERCEPT), ...); \
^ Needs BOOST_PP_COMMA_IF( BOOST_PP_DEC( arity ) ) in order to work with an arity of 1.
typedef typename add_reference< typename mpl::if_< is_fundamental<T> ^^^^^^^^^^^^^^
This detection does not cover all cases. However, it's expressive enough to get the idea and free of obvious mistakes, now - I hope ;+).

My apologies - please, just forget what I've posted.
no_overload_tag function( \ BOOST_PP_ENUM_PARAMS(BOOST_PP_DEC(arity) \ , any BOOST_PP_INTERCEPT)...); \
GCC allows this for binary operators (even in ansi mode), making me jump to conclusions - couldn't find anything in the standard backing it. Further I doubt there is a chance of weakening the "no-match-overload" in any other way to match worse than a function with one user defined conversion per argument. Sorry for that !

From: "Tobias Schwinger" <tschwinger@neoscientists.org>
My apologies - please, just forget what I've posted.
no_overload_tag function( \ BOOST_PP_ENUM_PARAMS(BOOST_PP_DEC(arity) \ , any BOOST_PP_INTERCEPT)...); \
GCC allows this for binary operators (even in ansi mode), making me jump to conclusions - couldn't find anything in the standard backing it.
Further I doubt there is a chance of weakening the "no-match-overload" in any other way to match worse than a function with one user defined conversion per argument.
Actually, there is...: --- Start --- #include <iostream> #include <boost/config.hpp> // For BOOST_STATIC_CONSTANT struct proxy { proxy(...); }; struct no_match_type {}; char op_test(...); char (&op_test(const no_match_type &))[2]; no_match_type operator+(const proxy &,const proxy &); template<class T,class U> struct has_plus_op { static T t; static U u; typedef has_plus_op type; BOOST_STATIC_CONSTANT(bool, value=sizeof(op_test(t+u))==1); }; class A { public: A(int); // Note user-defined conversion }; class B {}; A operator+(A,A); int main() { std::cout << has_plus_op<int,int>::value << "\n"; // Sure std::cout << has_plus_op<int,double>::value << "\n"; // No sweat (standard conversion - promotion) std::cout << has_plus_op<int,A>::value << "\n"; // Again, no problem UDC int -> A takes preference to ... std::cout << has_plus_op<A,B>::value << "\n"; // Nope } --- End --- This shows that using "proxy" allows implicit promotions/conversions, as well as user-defined conversions, to take place as part of the overload resolution. You can't use "..." as an operator parameter (as you found out), but you _can_ get around it like this. ;) The above is the "modified" isAddable that I mentioned in the OP, and which is found (with appropriate namespace protection) in operator_traits/detail/binop.hpp. It's good to be back. ;) Regards, Terje

Terje Slettebø wrote:
From: "Tobias Schwinger" <tschwinger@neoscientists.org> Further I doubt there is a chance of weakening the "no-match-overload" in any other way to match worse than a function with one user defined conversion per argument.
Actually, there is...: [...]
First of all thanks for taking the time to write such an encouraging reply to my capitulation ;+). It seems to me that we are referring to different parts of this discussion: Daniel Wallin writes:
check_add<float, int*>
Would trigger a compilation error otherwise.
'has_plus_op<float,int*>' fails for the same reason (the expression inside the function call becomes invalid). I tried putting the arguments inside the expression into classes with an implicit conversion operator - but failed coming up with a "no match overload" weak enough. According to 13.1-3 I don't think there is. 'enable_if' could probably do the trick - using a modern compiler supporting it, that is. Another solution could be to hard-wire the cases which cause errors into specializations (discarding Daniel's idea of a generalized facility or using a hell of a lot preprocessor metaprogramming). Best regards, Tobias

From: "Tobias Schwinger" <tschwinger@neoscientists.org>
Terje Slettebø wrote:
From: "Tobias Schwinger" <tschwinger@neoscientists.org> Further I doubt there is a chance of weakening the "no-match-overload" in any other way to match worse than a function with one user defined conversion per argument.
Actually, there is...: [...]
First of all thanks for taking the time to write such an encouraging reply to my capitulation ;+).
It seems to me that we are referring to different parts of this discussion:
Ah, yes, sorry about that.
Daniel Wallin writes:
check_add<float, int*>
Would trigger a compilation error otherwise.
'has_plus_op<float,int*>' fails for the same reason (the expression inside the function call becomes invalid).
I tried putting the arguments inside the expression into classes with an implicit conversion operator - but failed coming up with a "no match overload" weak enough. According to 13.1-3 I don't think there is.
Right, especially 13.3.1.2: "If no operand of an operator in an expression has a type that is a class or an enumeration, the operator is assumed to be a built-in operator and interpreted according to clause 5." This means that if you do float + int *, it won't even consider an overloaded operator+, and you get an error.
'enable_if' could probably do the trick - using a modern compiler supporting it, that is.
Yes. As mentioned in another posting, approach taken in the library I uploaded is to first check the cases with built-in types, and then - if it passes that check - do a check using an overloaded operator+, taking a type that is convertible from anything (...). Regards, Terje

"Daniel Wallin" <dalwan01@student.umu.se> wrote in message news:cht2ik$kcr$1@sea.gmane.org...
Jonathan Turkanis wrote:
"David Abrahams" <dave@boost-consulting.com> wrote in message
Don't you think it's time we generalized the hack into a nice macro that generates the right code?
I think so. In addition to prefix and postfix unary operators, there should be a binary version, too, right?
I have been toying with a more general solution to this problem, where one can do things like:
check_dereference<T, is_convertible<mpl::_, U&> > check_add<T, T, is_convertible<mpl::_, T> >
etc. I haven't used it anywhere yet, so I can't say if it's useful in practice, but at least it was fun implementing. ;)
I know the feeling. ;-) What I'd really like to be able to do is test assignability. Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> writes:
"Daniel Wallin" <dalwan01@student.umu.se> wrote in message news:cht2ik$kcr$1@sea.gmane.org...
Jonathan Turkanis wrote:
"David Abrahams" <dave@boost-consulting.com> wrote in message
Don't you think it's time we generalized the hack into a nice macro that generates the right code?
I think so. In addition to prefix and postfix unary operators, there should be a binary version, too, right?
I have been toying with a more general solution to this problem, where one can do things like:
check_dereference<T, is_convertible<mpl::_, U&> > check_add<T, T, is_convertible<mpl::_, T> >
etc. I haven't used it anywhere yet, so I can't say if it's useful in practice, but at least it was fun implementing. ;)
I know the feeling. ;-)
What I'd really like to be able to do is test assignability.
Forget it. Anything that can only be a member function is out. Also anything that's implicitly-generated and can only be disabled by making it private is out. operator= fails both tests :( -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"David Abrahams" <dave@boost-consulting.com> wrote in message news:ud60sjqg2.fsf@boost-consulting.com...
"Jonathan Turkanis" <technews@kangaroologic.com> writes:
What I'd really like to be able to do is test assignability.
Forget it. Anything that can only be a member function is out. Also anything that's implicitly-generated and can only be disabled by making it private is out. operator= fails both tests :(
I was just dreaming .... Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:chsujj$85u$1@sea.gmane.org...
"David Abrahams" <dave@boost-consulting.com> wrote in message news:ullfibawd.fsf@boost-consulting.com...
Don't you think it's time we generalized the hack into a nice macro that generates the right code?
I think so. In addition to prefix and postfix unary operators, there
should be a
binary version, too, right?
Jonathan
I have just posted macros for unary operators like the ones you describe. Check out the 'is_dereferenceable' folder in the YahooGroups Files section. The three metafunctions that I described earlier can now be generated in a single line of code: BOOST_IS_XXXABLE_PREFIX(dereference, *) I think that the macros for the binary operators will only be slightly more difficult to implement. Thanks, Alex

David Abrahams" <dave@boost-consulting.com> wrote in message news:ullfibawd.fsf@boost-consulting.com...
Don't you think it's time we generalized the hack into a nice macro that generates the right code?
Agreed. That way we could have utilities like these to check for all unary operators. In fact, I don't see any reason why the same idea couldn't be applied to binary operators. I would think that the usage would look something like this: DEFINE_UNARY_OPERATOR_TEST(is_dereferenceable, *); DEFINE_BINARY_OPERATOR_TEST(is_addable, +); Then you could do is_dereferenceable<T> or is_addable<T1, T2> where the latter expression is true if 't1 + t2' is well-formed, where 't1' is an instance of 'T1 &' and 't2' is an instance of 'T2 &'. Is this what you had in mind, or close to it? -- Alex Chovanec NIF and Engineering Division Computing Applications and Research Department Lawrence Livermore National Laboratory

"Alex Chovanec" <achovane@engin.umich.edu> wrote in message news:chr3k4$1b1$1@sea.gmane.org... | Please let me know if these are of interest to anyone. The big question is--- if I should use them as concept-checks where iterators are expected--- how portable are they? br Thorsten btw: in general I think they are useful

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message news:chsi4c$5mo$1@sea.gmane.org...
"Alex Chovanec" <achovane@engin.umich.edu> wrote in message news:chr3k4$1b1$1@sea.gmane.org...
| Please let me know if these are of interest to anyone.
The big question is--- if I should use them as concept-checks where iterators are expected--- how portable are they?
I've used is_incrementable successfully on Borland 5.x, which says a lot. I can't remember about VC6. Jonathan

"Jonathan Turkanis" <technews@kangaroologic.com> writes:
"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message news:chsi4c$5mo$1@sea.gmane.org...
"Alex Chovanec" <achovane@engin.umich.edu> wrote in message news:chr3k4$1b1$1@sea.gmane.org...
| Please let me know if these are of interest to anyone.
The big question is--- if I should use them as concept-checks where iterators are expected--- how portable are they?
I've used is_incrementable successfully on Borland 5.x, which says a lot. I can't remember about VC6.
It works. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message news:chsi4c$5mo$1@sea.gmane.org...
"Alex Chovanec" <achovane@engin.umich.edu> wrote in message news:chr3k4$1b1$1@sea.gmane.org...
The big question is--- if I should use them as concept-checks where iterators are expected--- how portable are they?
In theory, they should be just as portable as 'is_lvalue_iterator' and 'is_incrementable', since I have applied the same workarounds that those metafunctions use. More specifically, I have compiled my unit test and seen it pass under these compilers: msvc-7.1 msvc-7.0 intel-win32-8.0 cw-8.3 borland-5.6.4 gcc-mingw-3.2 I think that I wrote a fairly comprehensive unit test, but if anyone else can think of additional tests to add, I would appreciate it. -- Alex Chovanec NIF and Engineering Division Computing Applications and Research Department Lawrence Livermore National Laboratory

"Thorsten Ottosen" <nesotto@cs.auc.dk> wrote in message news:chsi4c$5mo$1@sea.gmane.org...
"Alex Chovanec" <achovane@engin.umich.edu> wrote in message news:chr3k4$1b1$1@sea.gmane.org...
The big question is--- if I should use them as concept-checks where iterators are expected--- how portable are they?
In theory, they should be just as portable as 'is_lvalue_iterator' and 'is_incrementable', since I have applied the same workarounds that those metafunctions use. More specifically, I have compiled my unit test and seen it pass under these compilers: msvc-7.1 msvc-7.0 intel-win32-8.0 cw-8.3 borland-5.6.4 gcc-mingw-3.2 I think that I wrote a fairly comprehensive unit test, but if anyone else can think of additional tests to add, I would appreciate it. -- Alex Chovanec NIF and Engineering Division Computing Applications and Research Department Lawrence Livermore National Laboratory
participants (8)
-
Alex Chovanec
-
Alex Chovanec
-
Daniel Wallin
-
David Abrahams
-
Jonathan Turkanis
-
Terje Slettebø
-
Thorsten Ottosen
-
Tobias Schwinger