【发布时间】:2015-06-28 18:04:19
【问题描述】:
我正在使用 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;
}
【问题讨论】:
-
C++11 但不是 C++14?你知道通过使用
std::function你是在断言你的协程可以被复制? -
我不完全理解你所说的“协程可以被复制”是什么意思。上面的代码有效,这正是我们在现实世界代码中使用这种模式的方式。我们不打算将协程对象从一个地方复制到另一个地方。
-
a
std::function<bool()>是一个对象,它被类型擦除直到被()调用,返回与bool兼容的类型并且可以被复制。两者都是std::function所要求的,并且该类型为其消费者提供了保证。如果您不打算同时承诺两者,则可能不应该使用它。 -
@Yakk,我承认可复制的协程听起来很奇怪。但是除了 std::function
之外,还有其他方法可以获取闭包对象的句柄吗?我使用 lambda/closure 部分是因为它可以自动捕获参数变量,我还可以将持久状态放入包含函数中,该函数将自动复制到闭包成员。这样,代码看起来很简单。否则我必须手动编写一个构造函数,这很不方便。 -
@Yakk,实际上我在微控制器上使用了这种模式,以替换 while-loop 状态机模式或消息/事件调度模式,这对于复杂的控制流来说非常尴尬和不自然。所有所谓的协程函数都是一个小型状态机。子功能是子状态机。 “调用堆栈”可以被视为一个线程。到目前为止,我的代码完美无缺。它们看起来都像上面的代码。但我不知道这是一种危险的做法,您能否指出在什么情况下,这种“可复制”功能很容易让事情变得混乱?
标签: c++11 lambda destructor c++14 coroutine