Lambda如何捕获自己以进行异步调用?

时间:2019-05-29 14:48:48

标签: c++ asynchronous recursion lambda

我必须在lambda内进行异步调用,并且一旦异步调用终止,我就必须调用lambda本身。

我尝试用代码解释我的问题:

const start = parseInt(request.params.start)
const end = parseInt(request.params.end)

const sql2 = 
     "SELECT b.gid, b.the_geom, b.cost_s, b.length_m FROM pgr_dijkstra('SELECT gid::bigint as id, source::bigint,"+
     "target::bigint, cost_s::double precision as cost," +
     "reverse_cost_s::double precision as reverse_cost FROM ways" + 
     "WHERE the_geom && ST_Expand((SELECT ST_Collect(the_geom)" + 
     "FROM ways_vertices_pgr WHERE id IN(" + start + "," + end + ")), 0.01)'," +
      start + "," + end + ") a LEFT JOIN ways b ON (a.edge = b.gid);"

typedef function<void(int id)> Callback;
AsyncWork1(Callback call, int id, string)
{
    //...
    call(id);
}

AsyncWork2(Callback call, int id, double, string)
{
    //...
    call(id);
}
void AsyncWorks(Callback final_callback, int id)
{
    Callback lambda = [&lambda, final_callback, id](int next_work) -> void
        {
            if(next_work == 1)
            {
                //...
                AsyncWork1(lambda, 2, "bla bla");
            }
            else if(next_work == 2)
            {
                //...
                //the lambda variable no longer exists
                AsyncWork2(lambda, 3, 0.0, "bla bla");
            }
            else if(next_work == 3)
            {
                //...
                final_callback(id);
            }
        };

    lambda(1);
}

问题在于,当代码从“ AsyncWorks(...)”函数退出时,局部变量“ lambda”不再存在。

我已经阅读了几个有关lambda递归的线程,但是我没有找到任何解决方案。

我该如何解决这个问题?

3 个答案:

答案 0 :(得分:2)

基本问题是C ++不会将Lambda的this指针公开给自己。

碰巧的是,在定义某项内容时,有许多语言无法引用其自身。使用称为“ Y Combinator”的技术将其固定在功能语言中。

C ++中的一个简单的y组合器看起来像:

template<class F>
struct y_combinator_t {
  F f;
  template<class...Args>
  auto operator()(Args&&...args)
  -> std::result_of_t< F&( y_combinator_t<F>&, Args&&... ) >
  {
    return f( *this, std::forward<Args>(args)... );
  }
};
template<class F>
y_combinator_t<std::decay_t<F>> y_combinate( F&& f ) {
  return {std::forward<F>(f)};
}

如果要使用f( *thisf( f,我有两种想法,有时我也可以。

使用:

void AsyncWorks(Callback final_callback, int id)
{
  Callback lambda = y_combinate(
    [final_callback, id]
    (auto& self, int next_work)
    -> void
    {
      if(next_work == 1) {
        //...
        AsyncWork1(self, 2, "bla bla");
      } else if(next_work == 2) {
        //...
        //the lambda variable no longer exists
        AsyncWork2(self, 3, 0.0, "bla bla");
      } else if(next_work == 3) {
        //...
        final_callback(id);
      }
    }
  );
  lambda(1);
}

基本上,我向lambda函数主体添加了一个隐式self参数。 operator()的调用者看不到此参数。

基于this post by myself进行修改的Y组合器。

答案 1 :(得分:0)

Lambda可以隐式捕获自身。演示如何?参见下面的代码,它计算阶乘值。

#include <iostream>

int (* factorial)( const int) = []( const int number)
{
    if( number > 1)
    {
        return number* factorial( number - 1);
    }
    else
    {
        return 1;
    }
};

int main(int , char *[])
{
    int fact = factorial( 7);
    std::cout<< "7! = "<< fact<< std::endl;
}
Output 7! = 5040

如果在lambda内部使用了任何变量,则lambda会隐式捕获它,如果未显式捕获它。由于存在内部lambda,因此可以使用引用自己的名称factorial
但是,如果使用int (* factorial)( const int) = []( const int number){//implementation };代替
auto

,则如下,

auto factorial = []( const int number){ //implementation };

然后g ++编译器给出以下错误,
error: use of ‘factorial’ before deduction of ‘auto’ return number* factorial( number - 1);

是因为未推导factorial的类型,对于相同的控件,auto不会推论块。对于factorial,名称auto仅在声明下方可用。

答案 2 :(得分:0)

@Y牛 为了理解您的答案,我不得不花一些时间研究许多c ++功能,值类别,右值,左值,移动构造函数,移动赋值运算符,可变参数模板,具有隐式转换的可变参数模板,result_of_t <>,decay_t <>,forward <>。

但是我还没有东西,为什么在这里加上'&'符号呢?

... std::result_of_t <F&(y_combinator_t<...

我还重写了您的解决方案,以使其针对我的情况更加具体,也使我(以及所有那些使用C ++的初学者)更容易阅读和理解

class y_combinator_t
{
public:
    function<void(y_combinator_t*, int, double, string)> callback;

    void operator()(int a, double b, string s)
    {
        this->callback(this, a, b, s);
    }
};

y_combinator_t combinator = {
    [id_work = 1]
    (y_combinator_t* _this, int a, double b, string s) mutable -> void
    {
        if(id_work == 1)
        {
            //...
            AsyncWork1(*_this, 2, 3.0, "bla bla");
            id_work = 2;
        }
        else if(id_work == 2)
        {
            //...
            AsyncWork2(*_this, 3, 0.0, "bla bla");
            id_work = 3;
        }
        else if(id_work == 3)
        {
            //...
        }
    }
};

//Start works
combinator(0, 0, "");