Problems related to std :: bind recursively on std :: function

For the function f(x, y, z) we can bind x to 0, getting the function g(y, z) == f(0, y, z) . We can continue this and get h() = f(0, 1, 2) .

In C ++ syntax, which will be

 #include <functional> #include <iostream> void foo(int a, long b, short c) { std::cout << a << b << c << std::endl; } int main() { std::function<void(int, long, short)> bar1 = foo; std::function<void(long, short)> bar2 = std::bind(bar1, 0, std::placeholders::_1, std::placeholders::_2); std::function<void(short)> bar3 = std::bind(bar2, 1, std::placeholders::_1); std::function<void()> bar4 = std::bind(bar3, 2); bar4(); // prints "012" return 0; } 

So far so good.

Now say that I want to do the same thing - bind the first argument to the function, return the new function and repeat this process until all the arguments are bound, but generalize it to work not only with function 3 as in the above example C ++, but with a function with an unknown * number of arguments.

* In C ++, there is such a thing as variable arguments, and in C ++ 11 there are variable templates. I mean the variable patterns here.

Basically, what I want to do is to write a function that takes any std::function and recursively binds the first argument to some value until all arguments are bound and the function can be called.

For simplicity, suppose std::function is a function that takes any integral arguments and returns void.

This code can be considered as a generalization of the previous code.

 #include <functional> #include <iostream> // terminating case of recursion void apply(std::function<void()> fun, int i) { fun(); } template<class Head, class... Tail> void apply(std::function<void(Head, Tail...)> f, int i) { std::function<void(Tail...)> g = std::bind(f, i); apply<Tail...>(g, ++i); } void foo(int a, long b, short c) { std::cout << a << b << c << std::endl; } int main() { std::function<void(int, long, short)> bar1 = foo; apply<int, long, short>(bar1, 0); return 0; } 

This code is wonderful. This is exactly what I want. It does not compile.

 main.cpp: In instantiation of 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = int; Tail = {long int, short int}]': main.cpp:24:40: required from here main.cpp:12:56: error: conversion from 'std::_Bind_helper<false, std::function<void(int, long int, short int)>&, int&>::type {aka std::_Bind<std::function<void(int, long int, short int)>(int)>}' to non-scalar type 'std::function<void(long int, short int)>' requested std::function<void(Tail...)> g = std::bind(f, i); ^ 

The problem is that you cannot just leave the call to std::placeholders in std::bind like that. They are required, and the number of placeholders in std::bind should match the number of unbound arguments in the function.

If we change the line

 std::function<void(Tail...)> g = std::bind(f, i); 

to

 std::function<void(Tail...)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2); 

we see that it successfully passes through the first call to apply() , but gets stuck on the second pass, because during the second pass g only one placeholder is needed, while we still have two of them in std::bind .

 main.cpp: In instantiation of 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = long int; Tail = {short int}]': main.cpp:13:30: required from 'void apply(std::function<void(Head, Tail ...)>, int) [with Head = int; Tail = {long int, short int}]' main.cpp:24:40: required from here main.cpp:12:102: error: conversion from 'std::_Bind_helper<false, std::function<void(long int, short int)>&, int&, const std::_Placeholder<1>&, const std::_Placeholder<2>&>::type {aka std::_Bind<std::function<void(long int, short int)>(int, std::_Placeholder<1>, std::_Placeholder<2>)>}' to non-scalar type 'std::function<void(short int)>' requested std::function<void(Tail...)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2); ^ 

There is a way to solve this using regular non-invariant templates, but it introduces a limit on the number of arguments to std::function . For example, this code only works if std::function has 3 or less arguments

(replace the apply functions in the previous code with them)

 // terminating case void apply(std::function<void()> fun, int i) { fun(); } template<class T0> void apply(std::function<void(T0)> f, int i) { std::function<void()> g = std::bind(f, i); apply(g, ++i); } template<class T0, class T1> void apply(std::function<void(T0, T1)> f, int i) { std::function<void(T1)> g = std::bind(f, i, std::placeholders::_1); apply<T1>(g, ++i); } template<class T0, class T1, class T2> void apply(std::function<void(T0, T1, T2)> f, int i) { std::function<void(T1, T2)> g = std::bind(f, i, std::placeholders::_1, std::placeholders::_2); apply<T1, T2>(g, ++i); } 

But the problem with this code is that I would have to define a new apply function to support std::function with 4 arguments, the same with 5 arguments 6 and so on. Not to mention that my goal was not to have a hard limit on the number of arguments. So this is unacceptable. I do not want him to have a limit.

I need to find a way to make the code of the variational pattern (the second code fragment) work.

If only std::bind not required to specify placeholders, then everything will work, but as std::bind works at the moment, we need to find a way to specify the correct number of placeholders.

Perhaps it would be useful to know that we can find the right amount of placeholders to indicate with C ++ 11 sizeof...

 sizeof...(Tail) 

but I couldnโ€™t get anything useful from it.

+6
source share
4 answers

First, stop using bind if you don't need to.

 // terminating case of recursion void apply(std::function<void()> fun, int i) { fun(); } // recursive case: template<class Head, class... Tail> void apply(std::function<void(Head, Tail...)> f, int i) { // create a one-shot lambda that binds the first argument to `i`: auto g = [&](Tail&&...tail) // by universal ref trick, bit fancy { return std::move(f)(std::move(i), std::forward<Tail>(tail)...);}; // recurse: apply<Tail...>(g, ++i); } 

next, only the type erases if you need:

 // `std::resukt_of` has a design flaw. `invoke` fixes it: template<class Sig,class=void>struct invoke{}; template<class Sig>using invoke_t=typename invoke<Sig>::type; // converts any type to void. Useful for sfinae, and may be in C++17: template<class>struct voider{using type=void;}; template<class T>using void_t=typename voider<T>::type; // implementation of invoke, returns type of calling instance of F // with Args... template<class F,class...Args> struct invoke<F(Args...), void_t<decltype(std::declval<F>()(std::declval<Args>()...))> >{ using type=decltype(std::declval<F>()(std::declval<Args>()...)); }; // tells you if F(Args...) is a valid expression: template<class Sig,class=void>struct can_invoke:std::false_type{}; template<class Sig> struct can_invoke<Sig,void_t<invoke_t<Sig>>> :std::true_type{}; 

Now we have a mechanism, a basic case:

 // if f() is a valid expression, terminate: template<class F, class T, class I, class=std::enable_if_t<can_invoke<F()>{}> > auto apply(F&& f, T&& t, I&&i)->invoke_t<F()> { return std::forward<F>(f)(); } 

which says: "if we can be called, just call f .

Next, the recursive case. It is based on C ++ 14 type inference:

 // if not, build lambda that binds first arg to t, then recurses // with i(t): template<class F, class T, class I, class=std::enable_if_t<!can_invoke<F()>{}, int>> > auto apply(F&& f, T&& t, I&&i) { // variardic auto lambda, C++14 feature, with sfinae support // only valid to call once, which is fine, and cannot leave local // scope: auto g=[&](auto&&...ts) // takes any number of params -> invoke_t< F( T, decltype(ts)... ) > // sfinae { return std::forward<F>(f)(std::forward<T>(t), decltype(ts)(ts)...); }; // recurse: return apply(std::move(g), i(t), std::forward<I>(i)); } 

If you want to increase, pass [](auto&&x){return x+1;} as the third argument.

If you don't want any changes, go to [](auto&&x){return x;} as the third argument.

None of these codes have been compiled, so there may be typos. I am also concerned about the recursion of using output like C ++ 14 return, which sometimes gets complicated.

+4
source

If you really need to use bind , you can define your own placeholder types, specializing in std::is_placeholder :

 template<int N> struct my_placeholder { static my_placeholder ph; }; template<int N> my_placeholder<N> my_placeholder<N>::ph; namespace std { template<int N> struct is_placeholder<::my_placeholder<N>> : std::integral_constant<int, N> { }; } 

The reason this is useful is because it then allows you to map an integer to a placeholder at compile time, which you can use with the integer_sequence trick:

 void apply(std::function<void()> fun, int i) { fun(); } template<class T, class... Ts> void apply(std::function<void(T, Ts...)> f, int i); template<class T, class... Ts, int... Is> void apply(std::function<void(T, Ts...)> f, int i, std::integer_sequence<int, Is...>) { std::function<void(Ts...)> g = std::bind(f, i, my_placeholder<Is + 1>::ph...); apply(g, ++i); } template<class T, class... Ts> void apply(std::function<void(T, Ts...)> f, int i) { apply(f, i, std::make_integer_sequence<int, sizeof...(Ts)>()); } 

Demo make_integer_sequence and friends are C ++ 14, but can be easily implemented in C ++ 11.

+2
source

If you are ready to give up std::bind (which was actually a bit of a hacky workaround for partial pre-C ++ 11 applications in my opinion), you can write it quite briefly:

 #include <functional> #include <iostream> // End recursion if no more arguments void apply(std::function<void()> f, int) { f(); } template <typename Head, typename ...Tail> void apply(std::function<void(Head, Tail...)> f, int i=0) { auto g = [=](Tail&& ...args){ f(i, std::forward<Tail>(args)...); }; apply(std::function<void(Tail...)>{g}, ++i); } void foo(int a, int b, int c, int d) { std::cout << a << b << c << d << "\n"; } int main() { auto f = std::function<void(int,int,int,int)>(foo); apply(f); } 

Tested while working with clang 3.4 and g ++ 4.8.2 in C ++ 11. Also on ideone .

+1
source

You do not need to use std::bind recursively to call some function with a tuple of parameters whose values โ€‹โ€‹can be estimated using the index parameter:

 #include <functional> #include <utility> template <typename... Types, std::size_t... indexes, typename Functor> void apply(std::function<void(Types...)> f, std::index_sequence<indexes...>, Functor&& functor) { f(static_cast<Types>(std::forward<Functor>(functor)(indexes))...); } template <typename... Types, typename Functor> void apply(std::function<void(Types...)> f, Functor&& functor) { apply(f, std::make_index_sequence<sizeof...(Types)>{}, std::forward<Functor>(functor)); } 

Usage example:

 void foo(int a, long b, short c) { std::cout << a << b << c << std::endl; } // ... std::function<void(int, long, short)> bar = foo; apply(bar, [](std::size_t index){ return (int)index; }); 

Live demo

As @ TC noted in his answer, std::make_index_sequence is C ++ 14, but it can be implemented in C ++ 11 .

0
source

Source: https://habr.com/ru/post/982508/


All Articles