[range] treating N-ary function with N-1 default args as unary

Hi, Some Boost.Range adaptors, such as 'transformed' or 'filtered', require a unary function. The documentation does not formally define "unary function", but it's pretty clear what it ought to be: a function (object) that's callable with one argument. Thus, to me, the following is a perfectly valid unary function: int foo(int a, int b = 0); as it is callable with one argument. However, Boost.Range does not accept such a function as an argument to 'transformed' or 'filtered'. For example, the following code: #include <boost/range/adaptor/transformed.hpp> int foo(int a, int b = 0); int main() { int A[5]; A | boost::adaptors::transformed(foo); } produces the following compiler errors (recent Boost trunk, with GCC 4.7): In file included from boost/preprocessor/iteration/detail/iter/forward1.hpp:52:0, from boost/utility/result_of.hpp:113, from boost/iterator/transform_iterator.hpp:23, from boost/range/adaptor/transformed.hpp:16, from test.cpp:1: boost/utility/detail/result_of_iterate.hpp: In instantiation of 'struct boost::detail::cpp0x_result_of_impl<int (* const(int&))(int, int)>': boost/utility/detail/result_of_iterate.hpp:46:8: required from 'struct boost::result_of<int (* const(int&))(int, int)>' boost/mpl/eval_if.hpp:38:31: required from 'struct boost::mpl::eval_if<boost::is_same<boost::use_default, boost::use_default>, boost::result_of<int (* const(int&))(int, int)>, boost::mpl::identity<boost::use_default> >' boost/iterator/iterator_adaptor.hpp:160:12: required from 'struct boost::detail::ia_dflt_help<boost::use_default, boost::result_of<int (* const(int&))(int, int)> >' boost/iterator/transform_iterator.hpp:50:17: required from 'struct boost::detail::transform_iterator_base<int (*)(int, int), int*, boost::use_default, boost::use_default>' c++/4.7.0/bits/stl_iterator_base_types.h:143:1: recursively required from 'constexpr const bool std::__has_iterator_category_helper<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >::value' c++/4.7.0/bits/stl_iterator_base_types.h:143:1: required from 'struct std::__has_iterator_category<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >' c++/4.7.0/bits/stl_iterator_base_types.h:160:12: required from 'struct std::iterator_traits<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >' boost/detail/iterator.hpp:81:8: required from 'struct boost::detail::iterator_traits<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >' boost/iterator/iterator_traits.hpp:30:75: required from 'struct boost::iterator_value<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >' boost/range/iterator_range_core.hpp:139:49: required from 'class boost::iterator_range<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >' boost/range/adaptor/transformed.hpp:25:16: required from 'struct boost::range_detail::transformed_range<int (*)(int, int), int [5]>' test.cpp:8:41: required from here boost/utility/detail/result_of_iterate.hpp:69:5: error: too few arguments to function In file included from boost/range/iterator_range_core.hpp:22:0, from boost/range/iterator_range.hpp:13, from boost/range/adaptor/transformed.hpp:15, from test.cpp:1: boost/iterator/iterator_traits.hpp: In instantiation of 'struct boost::iterator_value<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >': boost/range/iterator_range_core.hpp:139:49: required from 'class boost::iterator_range<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >' boost/range/adaptor/transformed.hpp:25:16: required from 'struct boost::range_detail::transformed_range<int (*)(int, int), int [5]>' test.cpp:8:41: required from here boost/iterator/iterator_traits.hpp:30:75: error: no type named 'value_type' in 'struct boost::detail::iterator_traits<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >' boost/iterator/iterator_traits.hpp: In instantiation of 'struct boost::iterator_difference<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >': boost/range/iterator_range_core.hpp:143:54: required from 'class boost::iterator_range<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >' boost/range/adaptor/transformed.hpp:25:16: required from 'struct boost::range_detail::transformed_range<int (*)(int, int), int [5]>' test.cpp:8:41: required from here boost/iterator/iterator_traits.hpp:49:80: error: no type named 'difference_type' in 'struct boost::detail::iterator_traits<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >' boost/iterator/iterator_traits.hpp: In instantiation of 'struct boost::iterator_reference<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >': boost/range/iterator_range_core.hpp:157:53: required from 'class boost::iterator_range<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >' boost/range/adaptor/transformed.hpp:25:16: required from 'struct boost::range_detail::transformed_range<int (*)(int, int), int [5]>' test.cpp:8:41: required from here boost/iterator/iterator_traits.hpp:36:74: error: no type named 'reference' in 'struct boost::detail::iterator_traits<boost::transform_iterator<int (*)(int, int), int*, boost::use_default, boost::use_default> >' Would it be possible to treat N-ary functions with N-1 default arguments as unary? Thanks, Nate

Some Boost.Range adaptors, such as 'transformed' or 'filtered', require a unary function. The documentation does not formally define "unary function", but it's pretty clear what it ought to be: a function (object) that's callable with one argument.
Thus, to me, the following is a perfectly valid unary function:
int foo(int a, int b = 0);
as it is callable with one argument.
However, Boost.Range does not accept such a function as an argument to 'transformed' or 'filtered'.
[snip]
Would it be possible to treat N-ary functions with N-1 default arguments as unary?
To motivate this request a bit further, suppose 'foo' is a function in some library, so that I can't just replace it with two overloads, and consider possible workarounds: 1. range | transformed(bind(foo, _1, let_me_repeat_the_default_arg_here)); 2. range | transformed([](let_me_write_out_the_type_of_foo's_argument_here x) { return foo(x); }) The first requires repeating the library function's default argument, which introduces a risk of the actual behaviour becoming out of sync with the desired behaviour if the default argument used by the library changes. (Note that simply bind(foo, _1) will not work - it produces similar errors). The second is messy (and would be even more so in C++03) and requires writing out the potentially long type name of the function's argument. I can't think of anything better at the moment. Regards, Nate

On 30 June 2012 14:21, Nathan Ridge <zeratul976@hotmail.com> wrote:
Some Boost.Range adaptors, such as 'transformed' or 'filtered', require a unary function. The documentation does not formally define "unary function", but it's pretty clear what it ought to be: a function (object) that's callable with one argument.
Thus, to me, the following is a perfectly valid unary function:
int foo(int a, int b = 0);
as it is callable with one argument.
Not really; it's still only called with two arguments, but the second is generated at the call site with the specified value if only one is specified. The default arguments are only part of the declaration; that's why you can specify them locally if you're calling a library function with the same arguments repeatedly: #include <iostream> using namespace std; // #include <some header> void foo(int x, int y); // but we always use y = 2 with foo void foo(int x, int y = 2); int main() { foo(1); } // and later link to the One Definition of foo void foo(int x, int y) { cout << "x: " << x << " y: " << y << endl; } https://gist.github.com/3023854
However, Boost.Range does not accept such a function as an argument to 'transformed' or 'filtered'.
[snip]
Would it be possible to treat N-ary functions with N-1 default arguments as unary?
To motivate this request a bit further, suppose 'foo' is a function in some library, so that I can't just replace it with two overloads, and consider possible workarounds:
1. range | transformed(bind(foo, _1, let_me_repeat_the_default_arg_here));
2. range | transformed([](let_me_write_out_the_type_of_foo's_argument_here x) { return foo(x); })
The first requires repeating the library function's default argument, which introduces a risk of the actual behaviour becoming out of sync with the desired behaviour if the default argument used by the library changes. (Note that simply bind(foo, _1) will not work - it produces similar errors).
The second is messy (and would be even more so in C++03) and requires writing out the potentially long type name of the function's argument.
I can't think of anything better at the moment.
Regards, Nate
I don't think this can be done generically; the function will get the arguments as in the most recent declaration at some arbitrary point, so you need to specify the point yourself; in this case, with the location of the bind call. I hope this is making sense, and that I'm using the correct terminology.

Le 30/06/12 06:07, Nathan Ridge a écrit :
Hi,
Some Boost.Range adaptors, such as 'transformed' or 'filtered', require a unary function. The documentation does not formally define "unary function", but it's pretty clear what it ought to be: a function (object) that's callable with one argument.
Thus, to me, the following is a perfectly valid unary function:
int foo(int a, int b = 0);
as it is callable with one argument.
However, Boost.Range does not accept such a function as an argument to 'transformed' or 'filtered'. For example, the following code:
#include<boost/range/adaptor/transformed.hpp>
int foo(int a, int b = 0);
int main() { int A[5]; A | boost::adaptors::transformed(foo); }
The following should work int unary(int a) {return foo(a)}; int main() { int A[5]; A | boost::adaptors::transformed(unary); } I agree that it will be interesting to have a way to make this bar transformation using a class template unary int main() { int A[5]; A | boost::adaptors::transformed(unary<foo>()); } Best, Vicente

AMDG On 06/30/2012 09:24 AM, Vicente J. Botet Escriba wrote:
Le 30/06/12 06:07, Nathan Ridge a écrit :
int foo(int a, int b = 0);
I agree that it will be interesting to have a way to make this bar transformation using a class template unary
int main() { int A[5]; A | boost::adaptors::transformed(unary<foo>()); }
Not possible. There's no way to preserve default arguments when taking a pointer to a function. In Christ, Steven Watanabe

Le 30/06/12 18:47, Steven Watanabe a écrit :
AMDG
On 06/30/2012 09:24 AM, Vicente J. Botet Escriba wrote:
Le 30/06/12 06:07, Nathan Ridge a écrit :
int foo(int a, int b = 0);
I agree that it will be interesting to have a way to make this bar transformation using a class template unary
int main() { int A[5]; A | boost::adaptors::transformed(unary<foo>()); }
Not possible. There's no way to preserve default arguments when taking a pointer to a function.
Yes I know. Could a lambda help on this context? int main() { int A[5]; A | boost::adaptors::transformed([](int a){ return foo(a);}); } Vicente

On 01/07/2012 01:18, Vicente J. Botet Escriba wrote:
Could a lambda help on this context?
int main() { int A[5]; A | boost::adaptors::transformed([](int a){ return foo(a);}); }
The OP already pointed this one out. It's not satisfying because it requires specifying the type of the first argument. Something you could do in C++11 int main() { struct foo_type { template<class Sig> struct result; template<class This, class A0> struct result<This(A0)> { typedef decltype(foo(declval<A0>())) type; }; template<class A0> typename result<foo_(A0&&)>::type operator()(A0&& a0) const { return foo(forward<A0>(a0)); } }; foo_type foo_; int A[5]; A | boost::adaptors::transformed(foo_); } The functor could be generated by a macro that would just take the function name and the number of arguments.

Mathias Gaunard wrote:
On 01/07/2012 01:18, Vicente J. Botet Escriba wrote:
Could a lambda help on this context?
int main() { int A[5]; A | boost::adaptors::transformed( [](int a) { return foo(a);}); }
The OP already pointed this one out. It's not satisfying because it requires specifying the type of the first argument.
Something you could do in C++11
int main() { struct foo_type { template<class Sig> struct result;
template<class This, class A0> struct result<This(A0)> { typedef decltype(foo(declval<A0>())) type; };
template<class A0> typename result<foo_(A0&&)>::type operator()( ^^^^ That clearly is a typo and I'm not certain what you actually meant.
A0&& a0) const { return foo(forward<A0>(a0)); } }; foo_type foo_; int A[5]; A | boost::adaptors::transformed(foo_); }
The functor could be generated by a macro that would just take the function name and the number of arguments.
Wouldn't there need to be a different macro for each number of actual arguments? That is, one for unary, one for binary, etc. or did you mean something else? _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components 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 03/07/2012 16:28, Stewart, Robert wrote:
Mathias Gaunard wrote:
struct foo_type { template<class Sig> struct result;
template<class This, class A0> struct result<This(A0)> { typedef decltype(foo(declval<A0>())) type; };
template<class A0> typename result<foo_(A0&&)>::type operator()(
^^^^ That clearly is a typo and I'm not certain what you actually meant.
foo_type, obviously.
Wouldn't there need to be a different macro for each number of actual arguments? That is, one for unary, one for binary, etc. or did you mean something else?
The macro would take the number of arguments as a parameter.
participants (6)
-
Mathias Gaunard
-
Nathan Ridge
-
Rowan James
-
Steven Watanabe
-
Stewart, Robert
-
Vicente J. Botet Escriba