disable_if conundrum

I have a function that returns a pair of iterators. There's also a version that takes a comparison predicate. template<class ForwardIterator, class Compare> std::pair<ForwardIterator, ForwardIterator> foo ( ForwardIterator first, ForwardIterator last ); template<class ForwardIterator, class Compare> std::pair<ForwardIterator, ForwardIterator> foo ( ForwardIterator first, ForwardIterator last, Compare comp ); I want to provide a range-based version of it. template<class Range> std::pair<typename boost::range_iterator<const Range>::type, typename boost::range_iterator<const Range>::type> foo ( const Range &r ); template<class Range, class Compare> std::pair<typename boost::range_iterator<const Range>::type, typename boost::range_iterator<const Range>::type> foo ( const Range &r, Compare comp ); Ok. There's a problem. If I call: foo ( first, last ) I get an error, because there are two perfectly good two argument candidates. Fine. Been there, seen that. I can use disable_if to make sure that the second range based version is only "active" when the arguments are different types. It's verbose, but it (usually) works template<class Range, class Compare> typename boost::disable_if_c<boost::is_same<Range, Compare>::value, std::pair<typename boost::range_iterator<const Range>::type, typename boost::range_iterator<const Range>::type> >::type foo ( const Range &r, Compare comp ); But this time it does not! I get a compile error telling me that the compiler can't deal with boost::range_iterator<XXX>::type, when XXX = some random iterator. It seems that the compiler wants to evaluate all the parameters of disable_if_c before deciding whether or not to SFINAE it (I guess that's not unreasonable, but it's not what I want) Apparently, all the other times that I did this, the return type of the function was not a dependent type of the template arguments. Any suggestions for a workaround here? -- Marshall -- Marshall Marshall Clow Idio Software <mailto:mclow.lists@gmail.com> A.D. 1517: Martin Luther nails his 95 Theses to the church door and is promptly moderated down to (-1, Flamebait). -- Yu Suzuki

On Mon, 5 Dec 2011, Marshall Clow wrote:
I have a function that returns a pair of iterators. There's also a version that takes a comparison predicate.
template<class ForwardIterator, class Compare> std::pair<ForwardIterator, ForwardIterator> foo ( ForwardIterator first, ForwardIterator last );
template<class ForwardIterator, class Compare> std::pair<ForwardIterator, ForwardIterator> foo ( ForwardIterator first, ForwardIterator last, Compare comp );
I want to provide a range-based version of it.
template<class Range> std::pair<typename boost::range_iterator<const Range>::type, typename boost::range_iterator<const Range>::type> foo ( const Range &r );
template<class Range, class Compare> std::pair<typename boost::range_iterator<const Range>::type, typename boost::range_iterator<const Range>::type> foo ( const Range &r, Compare comp );
Ok. There's a problem. If I call: foo ( first, last ) I get an error, because there are two perfectly good two argument candidates.
Fine. Been there, seen that. I can use disable_if to make sure that the second range based version is only "active" when the arguments are different types. It's verbose, but it (usually) works
template<class Range, class Compare> typename boost::disable_if_c<boost::is_same<Range, Compare>::value, std::pair<typename boost::range_iterator<const Range>::type, typename boost::range_iterator<const Range>::type> >::type foo ( const Range &r, Compare comp );
But this time it does not! I get a compile error telling me that the compiler can't deal with boost::range_iterator<XXX>::type, when XXX = some random iterator. It seems that the compiler wants to evaluate all the parameters of disable_if_c before deciding whether or not to SFINAE it (I guess that's not unreasonable, but it's not what I want)
Apparently, all the other times that I did this, the return type of the function was not a dependent type of the template arguments.
Any suggestions for a workaround here?
Look at lazy_disable_if -- it doesn't get the nested "type" member of the type you give it unless the condition evaluates to false. -- Jeremiah Willcock

On Mon, Dec 5, 2011 at 11:30 AM, Jeremiah Willcock <jewillco@osl.iu.edu>wrote:
On Mon, 5 Dec 2011, Marshall Clow wrote:
I have a function that returns a pair of iterators.
There's also a version that takes a comparison predicate.
template<class ForwardIterator, class Compare> std::pair<ForwardIterator, ForwardIterator> foo ( ForwardIterator first, ForwardIterator last );
template<class ForwardIterator, class Compare> std::pair<ForwardIterator, ForwardIterator> foo ( ForwardIterator first, ForwardIterator last, Compare comp );
I want to provide a range-based version of it.
template<class Range> std::pair<typename boost::range_iterator<const Range>::type, typename boost::range_iterator<const Range>::type> foo ( const Range &r );
template<class Range, class Compare> std::pair<typename boost::range_iterator<const Range>::type, typename boost::range_iterator<const Range>::type> foo ( const Range &r, Compare comp );
Ok. There's a problem. If I call: foo ( first, last ) I get an error, because there are two perfectly good two argument candidates.
Fine. Been there, seen that. I can use disable_if to make sure that the second range based version is only "active" when the arguments are different types. It's verbose, but it (usually) works
template<class Range, class Compare> typename boost::disable_if_c<boost::is_**same<Range, Compare>::value, std::pair<typename boost::range_iterator<const Range>::type, typename boost::range_iterator<const Range>::type> >::type foo ( const Range &r, Compare comp );
But this time it does not! I get a compile error telling me that the compiler can't deal with boost::range_iterator<XXX>::**type, when XXX = some random iterator. It seems that the compiler wants to evaluate all the parameters of disable_if_c before deciding whether or not to SFINAE it (I guess that's not unreasonable, but it's not what I want)
Apparently, all the other times that I did this, the return type of the function was not a dependent type of the template arguments.
Any suggestions for a workaround here?
Look at lazy_disable_if -- it doesn't get the nested "type" member of the type you give it unless the condition evaluates to false.
Just to supplement: also use an extra level of indirection, a metafunction that maps Range to the return type of foo. So will look like boost::lazy_disable_if_c< condition, foo_result< Range > >::type. - Jeff

On Dec 5, 2011, at 11:39 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Dec 5, 2011 at 11:30 AM, Jeremiah Willcock <jewillco@osl.iu.edu>wrote:
On Mon, 5 Dec 2011, Marshall Clow wrote:
I have a function that returns a pair of iterators.
There's also a version that takes a comparison predicate.
[ snip]
I want to provide a range-based version of it.
[ snip ]
Ok. There's a problem. If I call: foo ( first, last ) I get an error, because there are two perfectly good two argument candidates.
Fine. Been there, seen that. I can use disable_if to make sure that the second range based version is only "active" when the arguments are different types. It's verbose, but it (usually) works
[ more snippage ]
But this time it does not! I get a compile error telling me that the compiler can't deal with boost::range_iterator<XXX>::**type, when XXX = some random iterator. It seems that the compiler wants to evaluate all the parameters of disable_if_c before deciding whether or not to SFINAE it (I guess that's not unreasonable, but it's not what I want)
Apparently, all the other times that I did this, the return type of the function was not a dependent type of the template arguments.
Any suggestions for a workaround here?
Look at lazy_disable_if -- it doesn't get the nested "type" member of the type you give it unless the condition evaluates to false.
Just to supplement: also use an extra level of indirection, a metafunction that maps Range to the return type of foo. So will look like boost::lazy_disable_if_c< condition, foo_result< Range > >::type.
Yeah, that didn't seem to work for me. So - here's some code. And here's some error messages (from clang 3.0): $ clang++ -I /Volumes/EyeFive/Marshall/Sources/boost/trunk disable_if.cpp In file included from disable_if.cpp:5: In file included from /Volumes/EyeFive/Marshall/Sources/boost/trunk/boost/range/begin.hpp:24: In file included from /Volumes/EyeFive/Marshall/Sources/boost/trunk/boost/range/iterator.hpp:23: /Volumes/EyeFive/Marshall/Sources/boost/trunk/boost/mpl/eval_if.hpp:60:26: error: no type named 'type' in 'boost::range_const_iterator<__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > >' typedef typename f_::type type; ~~~~~~~~~~~~~^~~~ /Volumes/EyeFive/Marshall/Sources/boost/trunk/boost/range/iterator.hpp:61:18: note: in instantiation of template class 'boost::mpl::eval_if_c<true, boost::range_const_iterator<__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > >, boost::range_mutable_iterator<const __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > > >' requested here mpl::eval_if_c< is_const<C>::value, ^ disable_if.cpp:29:37: note: in instantiation of template class 'boost::range_iterator<const __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > >' requested here typedef std::pair<typename boost::range_iterator<const Range>::type, ^ disable_if.cpp:45:20: note: in instantiation of template class 'detail::range_pair<__gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > > >' requested here typename detail::range_pair<Range>::type> ^ disable_if.cpp:47:1: note: while substituting deduced template arguments into function template 'Foo' [with Range = __gnu_cxx::__normal_iterator<int *, std::vector<int, std::allocator<int> > >, Compare = ] Foo ( const Range &r, Compare comp ) ^ disable_if.cpp:83:2: note: in instantiation of function template specialization 'test_sequence_no_comparison<std::vector<int, std::allocator<int> > >' requested here test_sequence_no_comparison ( v ); // even # of elements ^ 1 error generated. Thanks! -- Marshall Marshall Clow Idio Software <mailto:mclow.lists@gmail.com> A.D. 1517: Martin Luther nails his 95 Theses to the church door and is promptly moderated down to (-1, Flamebait). -- Yu Suzuki

On Mon, 5 Dec 2011, Marshall Clow wrote:
On Dec 5, 2011, at 11:39 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Dec 5, 2011 at 11:30 AM, Jeremiah Willcock <jewillco@osl.iu.edu>wrote:
On Mon, 5 Dec 2011, Marshall Clow wrote:
I have a function that returns a pair of iterators.
There's also a version that takes a comparison predicate.
[ snip]
I want to provide a range-based version of it.
[ snip ]
Ok. There's a problem. If I call: foo ( first, last ) I get an error, because there are two perfectly good two argument candidates.
Fine. Been there, seen that. I can use disable_if to make sure that the second range based version is only "active" when the arguments are different types. It's verbose, but it (usually) works
[ more snippage ]
But this time it does not! I get a compile error telling me that the compiler can't deal with boost::range_iterator<XXX>::**type, when XXX = some random iterator. It seems that the compiler wants to evaluate all the parameters of disable_if_c before deciding whether or not to SFINAE it (I guess that's not unreasonable, but it's not what I want)
Apparently, all the other times that I did this, the return type of the function was not a dependent type of the template arguments.
Any suggestions for a workaround here?
Look at lazy_disable_if -- it doesn't get the nested "type" member of the type you give it unless the condition evaluates to false.
Just to supplement: also use an extra level of indirection, a metafunction that maps Range to the return type of foo. So will look like boost::lazy_disable_if_c< condition, foo_result< Range > >::type.
Yeah, that didn't seem to work for me. So - here's some code. And here's some error messages (from clang 3.0):
Your call to range_pair was not lazy enough -- on line 46, try "detail::range_pair<Range>" instead of "typename detail::range_pair<Range>::type". That worked for me at least on GCC 4.4. -- Jeremiah Willcock

On Dec 5, 2011, at 12:20 PM, Jeremiah Willcock wrote:
On Mon, 5 Dec 2011, Marshall Clow wrote: On Dec 5, 2011, at 11:39 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Dec 5, 2011 at 11:30 AM, Jeremiah Willcock <jewillco@osl.iu.edu>wrote:
On Mon, 5 Dec 2011, Marshall Clow wrote:
I have a function that returns a pair of iterators.
There's also a version that takes a comparison predicate.
[ snip]
I want to provide a range-based version of it.
[ snip ]
Ok. There's a problem. If I call: foo ( first, last ) I get an error, because there are two perfectly good two argument candidates.
Fine. Been there, seen that. I can use disable_if to make sure that the second range based version is only "active" when the arguments are different types. It's verbose, but it (usually) works
[ more snippage ]
But this time it does not! I get a compile error telling me that the compiler can't deal with boost::range_iterator<XXX>::**type, when XXX = some random iterator. It seems that the compiler wants to evaluate all the parameters of disable_if_c before deciding whether or not to SFINAE it (I guess that's not unreasonable, but it's not what I want)
Apparently, all the other times that I did this, the return type of the function was not a dependent type of the template arguments.
Any suggestions for a workaround here?
Look at lazy_disable_if -- it doesn't get the nested "type" member of the type you give it unless the condition evaluates to false.
Just to supplement: also use an extra level of indirection, a metafunction that maps Range to the return type of foo. So will look like boost::lazy_disable_if_c< condition, foo_result< Range > >::type.
Yeah, that didn't seem to work for me. So - here's some code. And here's some error messages (from clang 3.0):
Your call to range_pair was not lazy enough -- on line 46, try "detail::range_pair<Range>" instead of "typename detail::range_pair<Range>::type". That worked for me at least on GCC 4.4.
Thanks! That compiles - but I don't understand why - or even what the return type ends up being. I notice that if I change the typedef named in detail:range_pair from 'type' to 'assds' it still compiles. But if I add the following code: bool comp ( int x ) { return x > 10; } and res = Foo ( v, comp ); I get a bunch of compilation errors. Updated code attached.

On Dec 5, 2011, at 12:53 PM, Marshall Clow wrote:
But if I add the following code:
bool comp ( int x ) { return x > 10; } and res = Foo ( v, comp );
I get a bunch of compilation errors.
My bad. Since the range-based versions of Foo take the range by const &, they return pairs of const_iterators. res is a pair of non-const iterators; you can't assign one to the other. Never mind. Thanks, everyone, for your help! -- Marshall

On Mon, Dec 5, 2011 at 1:11 PM, Marshall Clow <mclow.lists@gmail.com> wrote:
On Dec 5, 2011, at 12:53 PM, Marshall Clow wrote:
But if I add the following code:
bool comp ( int x ) { return x > 10; } and res = Foo ( v, comp );
I get a bunch of compilation errors.
My bad. Since the range-based versions of Foo take the range by const &, they return pairs of const_iterators. res is a pair of non-const iterators; you can't assign one to the other.
...which suggests (to me) that you *may* want to provide a reference-to-non-const overload of Foo *even if* Foo does not modify its argument, since this will make it easier to mutate based on the result of Foo (if, e.g., the result is an iterator or pair of iterators). - Jeff

On Dec 5, 2011, at 1:40 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Dec 5, 2011 at 1:11 PM, Marshall Clow <mclow.lists@gmail.com> wrote:
On Dec 5, 2011, at 12:53 PM, Marshall Clow wrote:
But if I add the following code:
bool comp ( int x ) { return x > 10; } and res = Foo ( v, comp );
I get a bunch of compilation errors.
My bad. Since the range-based versions of Foo take the range by const &, they return pairs of const_iterators. res is a pair of non-const iterators; you can't assign one to the other.
...which suggests (to me) that you *may* want to provide a reference-to-non-const overload of Foo *even if* Foo does not modify its argument, since this will make it easier to mutate based on the result of Foo (if, e.g., the result is an iterator or pair of iterators).
Yes. I had come to that conclusion already. I would like Foo ( v.begin (), v.end ()); and Foo ( v ); to return the same results (or, at least, compatible results). -- Marshall Marshall Clow Idio Software <mailto:mclow.lists@gmail.com> A.D. 1517: Martin Luther nails his 95 Theses to the church door and is promptly moderated down to (-1, Flamebait). -- Yu Suzuki

AMDG On 12/05/2011 12:15 PM, Marshall Clow wrote:
On Dec 5, 2011, at 11:39 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Dec 5, 2011 at 11:30 AM, Jeremiah Willcock <jewillco@osl.iu.edu>wrote:
Look at lazy_disable_if -- it doesn't get the nested "type" member of the type you give it unless the condition evaluates to false.
Just to supplement: also use an extra level of indirection, a metafunction that maps Range to the return type of foo. So will look like boost::lazy_disable_if_c< condition, foo_result< Range > >::type.
Yeah, that didn't seem to work for me. So - here's some code. And here's some error messages (from clang 3.0):
The second argument to lazy_disable_if should be detail::range_pair<Range>, not typename detail::range_pair<Range>::type In Christ, Steven Watanabe
participants (4)
-
Jeffrey Lee Hellrung, Jr.
-
Jeremiah Willcock
-
Marshall Clow
-
Steven Watanabe