是否可以覆盖C ++ 11 lambda / closure析构函数?

时间:2015-04-21 14:05:17

标签: c++11 lambda destructor c++14 coroutine

我正在使用C ++ 11 lambda / closure功能,试图编写一个半协程(生成器)函数。

以下代码是我的实验。它们按预期工作(GCC 4.9 / Linux)。

我已经阅读了几篇介绍C ++ lambda / closure的在线文章,现在我将lambda函数理解为函子或函数对象。

我的问题是:我在哪里可以将析构函数代码放入闭包对象?如何覆盖默认的析构函数?

我需要这个函数,因为当我调用函数构造并返回闭包对象(代表一个协程)时,一些协同程序可能会请求某些系统资源。但是如果调用者函数决定取消/终止操作,则应该有一种机制来通知协同程序对象采取某些操作。放置代码的理想位置将在析构函数中,但我找不到如何。

有什么建议吗?

#include <iostream>
#include <functional>
#include <time.h>

#define CONCAT01(a, b)                      a##b
#define CONCAT02(a, b)                      CONCAT01(a, b)
#define YIELD()                             \
  do {                                      \
    CONCAT02(ENTRY, __LINE__):              \
    if (EP == && CONCAT02(ENTRY, __LINE__)) \
      EP = 0;                               \
    else                                    \
    {                                       \
      EP = && CONCAT02(ENTRY, __LINE__);    \
      return true;                          \
    }                                       \
  } while (0)

using namespace std;

typedef std::function<bool(void)> coroutine;

coroutine add(int a, int b, int * ret)
{
  timespec start;
  clock_gettime(CLOCK_REALTIME, &start);

  return [=]() mutable -> bool
  {
    timespec now;
    while(1) {
      clock_gettime(CLOCK_REALTIME, &now);
      if (now.tv_sec - start.tv_sec > 1)
        break;

      return true;
    }
    * ret = a + b;
    cout << "add " << a << " " << b << " equals: " << a + b << endl;
    return false;
  };
}

coroutine sum(int a, int b, int c, int * ret)
{
  void* EP = 0;
  coroutine co = 0;
  int tmp = 0;

  return [=] () mutable -> bool
  {
    if (EP) goto *EP;

    co = add(a, b, &tmp);
    while(co())
      YIELD();

    co = add(tmp, c, ret);
    while(co())
      YIELD();

    return false;
  };
}

int main()
{
  int ret;
  coroutine c = sum(1, 2, 4, &ret);
  while (c())
    {
      sleep(1);
      cout << "wakeup" << endl;
    }

  cout << "final: " << ret << endl;

  return 0;
}

1 个答案:

答案 0 :(得分:4)

这是一个包含任务和销毁任务的对象:

template<class F, class D>
struct coroutine {
  F f;
  D d;
  template<class...Args>
  std::result_of_t<F&(Args...)>
  operator()(Args&&...args){
    return f(std::forward<Args>(args)...);
  }
  ~coroutine() {
    d();
  }
};

它的工厂功能:

template<class F, class D>
coroutine< std::decay_t<F>, std::decay_t<D> >
make_coroutine( F&& f, D&& d ) {
  return {std::forward<F>(f), std::forward<D>(d)};
}

一些辅助宏:

#define COROUTINE_STATE ED=(void*)0
#define START_COROUTINE if (ED) goto *ED
#define FINISH_COROUTINE return false

auto add(int a, int b, int * ret) {
  timespec start;
  clock_gettime(CLOCK_REALTIME, &start);

  return make_coroutine([=]() mutable
  {
    timespec now;
    while(true) {
      clock_gettime(CLOCK_REALTIME, &now);
      if (now.tv_sec - start.tv_sec > 1)
        break;

      return true;
    }
    *ret = a + b;
    cout << "add " << a << " " << b << " equals: " << a + b << endl;
    return false;
  },
  []{
    cout << "adder destroyed\n";
  });
}
auto sum(int a, int b, int c, int * ret)
{
  std::function<bool()> co;
  int tmp = 0;

  return make_coroutine([=,COROUTINE_STATE] () mutable
    {
      START_COROUTINE;
      co = add(a, b, &tmp);
      while(co())
        YIELD();

      co = add(tmp, c, ret);
      while(co())
        YIELD();

      FINISH_COROUTINE;
    },
    []{
      std::cout << "winter is coming\n";
    }
  );
}

请注意,在此设计中,析构函数无法访问“body”中的任何状态更改,例如ED

您捕获COROUTINE_STATE以获取ED指针。然后,您START_COROUTINE;执行自动分支,YIELD()插入跳转点,FINISH_COROUTINE;结束协程。

缺乏共享状态是一个严重的问题。身体可以访问的co与驱逐舰可以访问的co完全不同=

我们可以通过向你的协程添加一个新的状态来解决这个问题,这个状态发生在FINISH_COROUTINE;之后或之后。注入访问的能力是棘手的 - 一种方法是将ED移出lambda捕获本身:

template<class F>
struct coroutine {
  void** ED = nullptr;
  F f;
  template<class...Args>
  std::result_of_t< F&(void**,Args...) >
  operator()(Args&&...args){
    return f(ED, std::forward<Args>(args)...);
  }
  ~coroutine() {
    void* end = 0;
    f(&end);
  }
};
template<class F>
coroutine<std::decay_t<F>>
make_coroutine(F&& f){return {std::forward<F>{f}};}
#define START_COROUTINE [=](void** ED)mutable{\
  if (ED){if (*ED)goto *ED;\
  else goto Cleanup;}
#define END_COROUTINE_BODY return false; Cleanup:
#define END_COROUTINE_CLEANUP }
#define END_COROUTINE END_COROUTINE_BODY; END_COROUTINE_CLEANUP

auto sum(int a, int b, int c, int * ret)
{
  std::function<bool()> co = 0;
  int tmp = 0;

  return
  START_COROUTINE
    co = add(a, b, &tmp);
    while(co())
      YIELD();

    co = add(tmp, c, ret);
    while(co())
      YIELD();
  END_COROUTINE_BODY
    std::cout << "winter is coming\n";
  END_COROUTINE_CLEANUP;
}

或类似的东西。我们显式地传递ED,如果它指向一个nullptr,我们认为它意味着“跳转到清理代码”。这将清理代码放在与正文相同的范围内。

我现在看到你的类型擦除到std::function可能有用。请注意,这种类型擦除需要内存分配开销(实际上),这是不幸的,并且每次调用都需要虚函数开销。

可悲的是,在我弄清楚之前,我重复使用了您的coroutine类型名称,因此直接开始使用std::function<bool()。道歉。