I wish to create a callback that recursively returns itself as a callback.
The suggested method to recurse is for the function to have a reference to itself:
std::function<void (int)> recursive_function = [&] (int recurse) {
std::cout << recurse << std::endl;
if (recurse > 0) {
recursive_function(recurse - 1);
}
};
This fails as soon as you return it from a function:
#include <functional>
#include <iostream>
volatile bool no_optimize = true;
std::function<void (int)> get_recursive_function() {
std::function<void (int)> recursive_function = [&] (int recurse) {
std::cout << recurse << std::endl;
if (recurse > 0) {
recursive_function(recurse - 1);
}
};
if (no_optimize) {
return recursive_function;
}
return [] (int) {};
}
int main(int, char **) {
get_recursive_function()(10);
}
which gives a segmentation fault after outputting 10
because the reference becomes invalid.
How do I do this? I have successfully used what I think is a Y Combinator (which I'll post as an answer), but it is hugely confusing. Is there a better way?
Other attempts
I have tried the boring approach of wrapping it in another layer of callbacks:
#include <functional>
#include <iostream>
#include <memory>
volatile bool no_optimize = true;
std::function<void (int)> get_recursive_function() {
// Closure to allow self-reference
auto recursive_function = [] (int recurse) {
// Actual function that does the work.
std::function<void (int)> function = [&] (int recurse) {
std::cout << recurse << std::endl;
if (recurse > 0) {
function(recurse - 1);
}
};
function(recurse);
};
if (no_optimize) {
return recursive_function;
}
return [] (int) {};
}
int main(int, char **) {
get_recursive_function()(10);
}
but this fails in the actual scenario, where the function is being delayed and called by an outer loop:
#include <functional>
#include <iostream>
#include <memory>
#include <queue>
volatile bool no_optimize = true;
std::queue<std::function<void (void)>> callbacks;
std::function<void (int)> get_recursive_function() {
// Closure to allow self-reference
auto recursive_function = [] (int recurse) {
// Actual function that does the work.
std::function<void (int)> function = [&] (int recurse) {
std::cout << recurse << std::endl;
if (recurse > 0) {
callbacks.push(std::bind(function, recurse - 1));
}
};
function(recurse);
};
if (no_optimize) {
return recursive_function;
}
return [] (int) {};
}
int main(int, char **) {
callbacks.push(std::bind(get_recursive_function(), 10));
while (!callbacks.empty()) {
callbacks.front()();
callbacks.pop();
}
}
which gives 10
, then 9
and then segmentation faults.
As you rightly pointed out, there is an invalid reference from the lambda capture
[&]
.Your returns are functors of various sorts, so I assume that the exact type of the return is not important, just that it behaves as a function would i.e. be callable.
If the
recursive_function
is wrapped in astruct
orclass
you can map the call operator to therecursive_function
member. A problem arises with the capture of thethis
variable. It'll be captured with thethis
on creation, so if the object is copied around a bit, the originalthis
may no longer be valid. So an appropriatethis
can be passed in to the function at execution time (thisthis
issue may not be a problem, but it depends heavily on when and how you call the function).Alternatively, if the
recursive_function
can bestatic
then declaring it as such in the original code sample may also do the trick for you.I wanted to add some generality to the answer above, i.e. make it a template;
How does this work? Basically it moves the recursion from inside the function (i.e. calling itself) to an object (i.e. the function operator on the object itself) to implement the recursion. In the
get_recursive_function
the result typerecursive<void (int)>
is used as the first argument to the recursive function. It isconst&
because I've implemented theoperator()
asconst
in line with most standard algorithms and the default for a lambda function. It does require some "co-operation" from the implementer of the function (i.e. the use of theme
parameter; itself being*this
) to get the recursion working, but for that price you get a recursive lambda that is not dependent on a stack reference.My current workaround, which is unfortunately complicated, is:
(I think this is a Y Combinator, but I am unsure.)
This is the base function; it does not call itself but an argument it is passed. This argument should be the recursive function itself.
So the wanted function can be made by applying the combinator to the almost-recursive function.
When
main
is run, this outputs10
,9
, ...,0
, as wanted.All problems in programming can be solved with another layer of indirection, except too many layers of indirection.
My goal is to create a type
recursive<void(int)>
that lets you easily create a recursive lambda. To do this, you pass in a lambda with signaturevoid(recursive<void(int)>, int)
-- the first argument is what you invoke in order to do your recursive call.I then tie it up in knots and make it a fully recursive function with signature
void(int)
.Here is my implementation of
recursive<Signature>
:That is, admittedly, pretty complicated. I did a bunch of things to make it more efficient, such a perfect forwarding. It also, unlike
std::function
, double checks that the type of the lambda you pass to it matches the signature it wants.I believe, but have not confirmed, that I made it friendly to make the lambdas signature
void(auto&&,int)
. Anyone know of a fully compliant C++1y online compiler?The above is just boilerplate. What matters is how it looks at point of use:
Here we do the popular
auto f = lambda
syntax. No need to directly store it in astd::function
.Then, we explicitly cast it to a
recursive<void(int)>
, which ties it up in knots and removes therecursive<void(int)>
argument tof
from the front, and exposes the signaturevoid(int)
.This does require that your lambda take
recursive<void(int)> self
as its first parameter, and do recursion through it, but that doesn't seem to harsh. If I wrote it just right it might work withauto&& self
as the first parameter, but I am unsure.recursive<?>
works for any signature, naturally.live example
And, with delayed calls in outer loop it still works. Note that I got rid of that global variable (it would work with it as a global variable, it just felt dirty to leave it in).
In C++1y, we can eliminate the type erasure &
shared_ptr
overhead you see above (with therecursive
object holding ashared_ptr<function<?>>
). You do have to provide the return value, as I cannot getresult_of
to untangle my mess:Then a slightly different implementation of
get_recursive_function
(where I added some state for fun):the use of
std::function
in the return value ofget_recursive_function
is optional -- you could useauto
in C++1y. There is still some overhead compared to a perfect version (where the lambda could access its ownoperator()
), because theoperator()
probably doesn't know that it is being recursively invoked on the same object when it invokesself
.It would be tempting to allow
operator()( blah )
within the body of a lambda to allow a recursive invocation of the lambda. It would probably break very little code.Since you already deal with
std::function
that add a bit of overhead all over the place, you can add memory persistence that will only add an indirection at the calling site with aunique_ptr
: